/* $Id: VBoxMPGaWddm.cpp $ */
/** @file
 * VirtualBox Windows Guest Mesa3D - Gallium driver interface for WDDM kernel mode driver.
 */

 /*
  * Copyright (C) 2016-2020 Oracle Corporation
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
  * available from http://www.virtualbox.org. This file is free software;
  * you can redistribute it and/or modify it under the terms of the GNU
  * General Public License (GPL) as published by the Free Software
  * Foundation, in version 2 as it comes in the "COPYING" file of the
  * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
  * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
  */

#include <tchar.h>
#include "VBoxMPWddm.h"
#include "VBoxMPMisc.h"
#include "VBoxMPVidPn.h"
#include "VBoxMPVModes.h"
#include "VBoxMPRegistry.h"
#include "wddm/VBoxMPTypes.h"
#include "gallium/VBoxMPGaWddm.h"
#include "gallium/VBoxMPGaUtils.h"
#include "gallium/VBoxMPGaExt.h"
#include "gallium/svga3d_cmd.h"
#include "gallium/svga3d_types.h"
#include "gallium/SvgaHw.h"
#include "gallium/SvgaFifo.h"
#include "gallium/Svga.h"
#include "trace.h"
#include "memobj.h"

static NTSTATUS UpdateChildStatus(PVBOXMP_DEVEXT pDevExt, BOOLEAN connect)
{
    PAGED_CODE();
    NTSTATUS Status = STATUS_SUCCESS;

    for (int i = 0; i < VBoxCommonFromDeviceExt(pDevExt)->cDisplays; ++i)
    {
        DXGK_CHILD_STATUS  ChildStatus;
        RtlZeroMemory(&ChildStatus, sizeof(ChildStatus));
        ChildStatus.Type = StatusConnection;
        ChildStatus.ChildUid = i;
        ChildStatus.HotPlug.Connected = connect;
        Status = pDevExt->u.primary.DxgkInterface.DxgkCbIndicateChildStatus(pDevExt->u.primary.DxgkInterface.DeviceHandle, &ChildStatus);
        if (Status != STATUS_SUCCESS)
        {
            LOGE("<---DxgkCbIndicateChildStatus failed with status 0x%x\n ", Status);
            return Status;
        }
    }
    return Status;
}

void GaAdapterStop(PVBOXMP_DEVEXT pDevExt)
{
    VBOXWDDM_EXT_GA *pGaDevExt = pDevExt->pGa;
    LOGE("Begin ga adapter stop\n");

    if (pGaDevExt)
    {
        VBoxWddmVModesRemoveAll(pDevExt);
        VBoxWddmVModesCleanup();
        UpdateChildStatus(pDevExt, FALSE);
        if (!RTListIsEmpty(&pGaDevExt->listHwRenderData))
        {
            GAHWRENDERDATA *pIter, *pNext;
            RTListForEachSafe(&pGaDevExt->listHwRenderData, pIter, pNext, GAHWRENDERDATA, node)
            {
                /* Delete the data. SvgaRenderComplete deallocates pIter. */
                RTListNodeRemove(&pIter->node);
                SvgaRenderComplete(pGaDevExt->hw.pSvga, pIter);
            }
        }

        /* Free fence objects. */
        GaFenceObjectsDestroy(pGaDevExt, NULL);
        GaFenceObjectPoolDestroy(pGaDevExt);
        if (pGaDevExt->hw.pSvga)
        {
            VBOXWDDM_EXT_VMSVGA *pSvga = pGaDevExt->hw.pSvga;
            /* Free GMRs. */
            SvgaRegionsDestroy(pSvga, NULL);
            if (RT_BOOL(pSvga->u32Caps & SVGA_CAP_DX))
            {
                /* Free the miniport mob at last. Can't use SvgaMobDestroy here because it tells the host to write a fence
                * value to this mob. */
                void *pvCmd = SvgaCmdBuf3dCmdReserve(pSvga, SVGA_3D_CMD_DESTROY_GB_MOB, sizeof(SVGA3dCmdDestroyGBMob), SVGA3D_INVALID_ID);
                if (pvCmd)
                {
                    SVGA3dCmdDestroyGBMob *pCmd = (SVGA3dCmdDestroyGBMob *)pvCmd;
                    pCmd->mobid = VMSVGAMOB_ID(pSvga->pMiniportMob);
                    SvgaCmdBufCommit(pSvga, sizeof(*pCmd));
                }
                else {
                    LOGE("Destory Miniport GB MOB failed\n");
                    AssertFailed();
                }
            }
            SvgaAdapterStop(pGaDevExt->hw.pSvga, &pDevExt->u.primary.DxgkInterface);
            pGaDevExt->hw.pSvga = NULL;
        }

        GaMemFree(pGaDevExt->pvImageTransCursor);
        pGaDevExt->pvImageTransCursor = NULL;

        GaMemFree(pGaDevExt);
        pDevExt->pGa = NULL;
    }
    LOGE("End ga adapter stop\n");
}

NTSTATUS GaAdapterStart(PVBOXMP_DEVEXT pDevExt)
{
    LOGE("Begin ga adapter start\n");

    NTSTATUS Status;

    if (pDevExt->enmHwType != VBOXVIDEO_HWTYPE_VMSVGA) {
        LOGE("HW type is not VMSVGA!\n");
        return STATUS_NOT_SUPPORTED;
    }

    VBOXWDDM_EXT_GA *pGaDevExt = (VBOXWDDM_EXT_GA *)GaMemAllocZero(sizeof(*pGaDevExt));
    if (!pGaDevExt) {
        LOGE("Alloc pGaDevExt failed!\n");
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RTListInit(&pGaDevExt->listHwRenderData);
    /* Init fence objects. */
    pGaDevExt->fenceObjects.u32SeqNoSource = 0;
    RTListInit(&pGaDevExt->fenceObjects.list);
    KeInitializeSpinLock(&pGaDevExt->fenceObjects.SpinLock);
    memset(pGaDevExt->fenceObjects.au32HandleBits, 0, sizeof(pGaDevExt->fenceObjects.au32HandleBits));
    ASMBitSet(pGaDevExt->fenceObjects.au32HandleBits, 0); /* Exclude id==0, it is for NULL. */

    Status = GaFenceObjectPoolInit();
    if (Status != STATUS_SUCCESS) {
        LOGE("GaFenceObjectPoolInit failed\n");
        goto err0;
    }

    pGaDevExt->pvImageTransCursor = (void *)GaMemAllocZero(PAGE_SIZE);
    if (!pGaDevExt->pvImageTransCursor) {
        LOGE("malloc mem for image transparent cursor failed\n");
        Status = VERR_NO_MEMORY;
        goto err1;
    }

    Status = SvgaAdapterStart(&pGaDevExt->hw.pSvga, &pDevExt->u.primary.DxgkInterface,
                              pDevExt->HwResources.phFIFO, pDevExt->HwResources.cbFIFO,
                              pDevExt->HwResources.phIO, pDevExt->HwResources.cbIO);
    if (Status != STATUS_SUCCESS) {
        LOGE("Alloc pGaDevExt failed!\n");
        goto err2;
    }

    pDevExt->pGa = pGaDevExt;

    KeMemoryBarrier();

    if (RT_BOOL(pGaDevExt->hw.pSvga->u32Caps & SVGA_CAP_DX)) {
        Status = svgaCreateMiniportMob(pGaDevExt->hw.pSvga);
        if (Status != STATUS_SUCCESS) {
            LOGE("Create MiniportMob failed\n");
            goto err3;
        }
    }

    LOGE("Start init VModes\n");
    VBoxWddmVModesInit(pDevExt, FALSE);
    UpdateChildStatus(pDevExt, TRUE);

    LOGE("End ga adapter start, status = 0x%x\n", Status);
    return Status;

err3:
    pDevExt->pGa = NULL;
err2:
    SvgaAdapterStop(pGaDevExt->hw.pSvga, &pDevExt->u.primary.DxgkInterface);
    KeMemoryBarrier();
    pGaDevExt->hw.pSvga = NULL;
    GaMemFree(pGaDevExt->pvImageTransCursor);
    pGaDevExt->pvImageTransCursor = NULL;
err1:
    GaFenceObjectPoolDestroy(pGaDevExt);
err0:
    GaMemFree(pGaDevExt);

    return Status;
}

NTSTATUS GaVidPnSourceCheckPos(PVBOXMP_DEVEXT pDevExt, UINT iSource)
{
    POINT Pos = {0};
    NTSTATUS Status = vboxWddmDisplaySettingsQueryPos(pDevExt, iSource, &Pos);
    if (NT_SUCCESS(Status)) {
        PVBOXWDDM_SOURCE pSource = &pDevExt->aSources[iSource];
        if (memcmp(&pSource->VScreenPos, &Pos, sizeof(Pos))) {
            pSource->VScreenPos = Pos;
            Status = GaVidPnSourceReport(pDevExt, pSource);
        }
    }
    return Status;
}

static inline VBOXVIDEOOFFSET vboxWddmAddrVRAMOffset(VBOXWDDM_ADDR const *pAddr)
{
    return (pAddr->offVram != VBOXVIDEOOFFSET_VOID && pAddr->SegmentId) ?
        (pAddr->SegmentId == 1 ? pAddr->offVram : 0) :
        VBOXVIDEOOFFSET_VOID;
}

NTSTATUS GaVidPnSourceReport(PVBOXMP_DEVEXT pDevExt, VBOXWDDM_SOURCE *pSource)
{
    NTSTATUS Status = STATUS_SUCCESS;

    VBOXVIDEOOFFSET offVRAM = vboxWddmAddrVRAMOffset(&pSource->AllocData.Addr);
    if (offVRAM == VBOXVIDEOOFFSET_VOID) {
        return STATUS_SUCCESS; /* Ignore such VidPn sources. */
    }

    VBOXWDDM_TARGET_ITER Iter;
    VBoxVidPnStTIterInit(pSource, pDevExt->aTargets, VBoxCommonFromDeviceExt(pDevExt)->cDisplays, &Iter);
    for (PVBOXWDDM_TARGET pTarget = VBoxVidPnStTIterNext(&Iter);
         pTarget;
         pTarget = VBoxVidPnStTIterNext(&Iter))
    {
        Status = GaScreenDefine(pDevExt->pGa,
                                (UINT32)offVRAM,
                                pTarget->u32Id,
                                pSource->VScreenPos.x, pSource->VScreenPos.y,
                                pSource->AllocData.SurfDesc.width, pSource->AllocData.SurfDesc.height,
                                RT_BOOL(pSource->bBlankedByPowerOff));
    }

    return Status;
}

NTSTATUS GaDefineCursor(PVBOXWDDM_EXT_GA pGaDevExt,
    UINT32 u32HotspotX,
    UINT32 u32HotspotY,
    UINT32 u32Width,
    UINT32 u32Height,
    UINT32 u32AndMaskDepth,
    UINT32 u32XorMaskDepth,
    void const* pvAndMask,
    UINT32 cbAndMask,
    void const* pvXorMask,
    UINT32 cbXorMask)
{
    VBOXWDDM_EXT_VMSVGA* pSvga = pGaDevExt->hw.pSvga;
    return SvgaDefineCursor(pSvga, u32HotspotX, u32HotspotY, u32Width, u32Height,
        u32AndMaskDepth, u32XorMaskDepth,
        pvAndMask, cbAndMask, pvXorMask, cbXorMask);
}

NTSTATUS GaDefineAlphaCursor(PVBOXWDDM_EXT_GA pGaDevExt,
    UINT32 u32HotspotX,
    UINT32 u32HotspotY,
    UINT32 u32Width,
    UINT32 u32Height,
    void const* pvImage,
    UINT32 cbImage)
{
    VBOXWDDM_EXT_VMSVGA* pSvga = pGaDevExt->hw.pSvga;
    return SvgaDefineAlphaCursor(pSvga, u32HotspotX, u32HotspotY, u32Width, u32Height,
        pvImage, cbImage);
}

NTSTATUS StoreResolutionToReg(UINT32 width, UINT32 height)
{
    VBOXMPCMNREGISTRY Registry;
    NTSTATUS Status = vboxWddmRegOpenKey(&Registry, VBOXWDDM_REG_RESOLUTIONS, GENERIC_WRITE);
    if (Status != STATUS_SUCCESS) {
        Status = vboxWddmRegCreateKey(&Registry, VBOXWDDM_REG_RESOLUTIONS, GENERIC_WRITE);
    }
    if (Status != STATUS_SUCCESS) {
        LOGE("Init Reg failed, status is 0x%x\n", Status);
        return Status;
    }
	wchar_t widthKeyName[32] = { 0 };
	wchar_t heightKeyName[32] = { 0 };
	wchar_t resolutionFlag[32] = { 0 };
	if (width * 3 / 4 == height) {
        swprintf_s(resolutionFlag, 32, L"%ls", L"_4_3");
	} else if (width * 2 / 3 == height) {
        swprintf_s(resolutionFlag, 32, L"%ls", L"_3_2");
	} else if (width * 67 / 103 == height) {
        swprintf_s(resolutionFlag, 32, L"%ls", L"_103_67");
	} else {
        LOGE("Resolution unknown\n");
    }

    swprintf_s(widthKeyName, 32, L"width%ls", resolutionFlag);
    swprintf_s(heightKeyName, 32, L"height%ls", resolutionFlag);

	Status = vboxWddmRegSetValueDword(Registry, widthKeyName, width);
    if (Status != STATUS_SUCCESS) {
        LOGE("Set width reg failed, status is %d\n", Status);
    }
	Status = vboxWddmRegSetValueDword(Registry, heightKeyName, height);
    if (Status != STATUS_SUCCESS) {
        LOGE("Set height reg failed, status is %d\n", Status);
    }
    Status = VBoxMPCmnRegFini(Registry);
    if (Status != STATUS_SUCCESS) {
        LOGE("Close reg failed, status is %d\n", Status);
    }
    return Status;
}

NTSTATUS GaScreenDefine(PVBOXWDDM_EXT_GA pGaDevExt,
                        UINT32 u32Offset,
                        UINT32 u32ScreenId,
                        INT32 xOrigin,
                        INT32 yOrigin,
                        UINT32 u32Width,
                        UINT32 u32Height,
                        bool fBlank)
{
    StoreResolutionToReg(u32Width, u32Height);
    return SvgaScreenDefine(pGaDevExt->hw.pSvga, u32Offset, u32ScreenId,
                            xOrigin, yOrigin, u32Width, u32Height, fBlank);
}

NTSTATUS GaScreenDestroy(PVBOXWDDM_EXT_GA pGaDevExt,
                         UINT32 u32ScreenId)
{
    return SvgaScreenDestroy(pGaDevExt->hw.pSvga, u32ScreenId);
}

static void vboxWddmRectCopy(void* pvDst, UINT32 cbDstBytesPerPixel, UINT32 cbDstPitch,
    void const* pvSrc, UINT32 cbSrcBytesPerPixel, UINT32 cbSrcPitch,
    RECT const* pRect)
{
    UINT8 *pu8Dst = (UINT8 *)pvDst;
    pu8Dst += pRect->top * cbDstPitch + pRect->left * cbDstBytesPerPixel;

    UINT8 const *pu8Src = (UINT8 *)pvSrc;
    pu8Src += pRect->top * cbSrcPitch + pRect->left * cbSrcBytesPerPixel;

    UINT32 const cbLine = (pRect->right - pRect->left) * cbDstBytesPerPixel;
    for (INT y = pRect->top; y < pRect->bottom; ++y)
    {
        memcpy(pu8Dst, pu8Src, cbLine);
        pu8Dst += cbDstPitch;
        pu8Src += cbSrcPitch;
    }
}

static BOOLEAN FindUpdateRect(
    _In_ ULONG             NumMoves,
    _In_ D3DKMT_MOVE_RECT* pMoves,
    _In_ ULONG             NumDirtyRects,
    _In_ PRECT             pDirtyRect,
    _Out_ PRECT pUpdateRect)
{
    PAGED_CODE();

    BOOLEAN updated = FALSE;

    if (pUpdateRect == NULL) return FALSE;

    if (NumMoves == 0 && NumDirtyRects == 0) {
        pUpdateRect->bottom = 0;
        pUpdateRect->left = 0;
        pUpdateRect->right = 0;
        pUpdateRect->top = 0;
    }

    for (ULONG i = 0; i < NumMoves; i++) {
        PRECT  pRect = &pMoves[i].DestRect;
        if (!updated) {
            *pUpdateRect = *pRect;
            updated = TRUE;
        } else {
            pUpdateRect->bottom = RT_MAX(pRect->bottom, pUpdateRect->bottom);
            pUpdateRect->left = RT_MIN(pRect->left, pUpdateRect->left);
            pUpdateRect->right = RT_MAX(pRect->right, pUpdateRect->right);
            pUpdateRect->top = RT_MIN(pRect->top, pUpdateRect->top);
        }
    }

    for (ULONG i = 0; i < NumDirtyRects; i++) {
        PRECT  pRect = &pDirtyRect[i];
        if (!updated) {
            *pUpdateRect = *pRect;
            updated = TRUE;
        } else {
            pUpdateRect->bottom = RT_MAX(pRect->bottom, pUpdateRect->bottom);
            pUpdateRect->left = RT_MIN(pRect->left, pUpdateRect->left);
            pUpdateRect->right = RT_MAX(pRect->right, pUpdateRect->right);
            pUpdateRect->top = RT_MIN(pRect->top, pUpdateRect->top);
        }
    }

    return updated;
}

static NTSTATUS gaSourceBlitToScreen(PVBOXMP_DEVEXT pDevExt,
                VBOXWDDM_SOURCE *pSource, RECT const *pRect)
{
    NTSTATUS Status = STATUS_SUCCESS;
    PVBOXWDDM_EXT_VMSVGA pSvga = pDevExt->pGa->hw.pSvga;

    VBOXWDDM_TARGET_ITER Iter;
    VBoxVidPnStTIterInit(pSource, pDevExt->aTargets,
        VBoxCommonFromDeviceExt(pDevExt)->cDisplays, &Iter);
    for (PVBOXWDDM_TARGET pTarget = VBoxVidPnStTIterNext(&Iter);
         pTarget; pTarget = VBoxVidPnStTIterNext(&Iter)) {
        Status = SvgaUpdate(pSvga, pRect->left, pRect->top,
                            pRect->right - pRect->left,
                            pRect->bottom - pRect->top);
    }

    return Status;
}

NTSTATUS APIENTRY GaDxgkDdiPresentDisplayOnly(const HANDLE hAdapter,
    const DXGKARG_PRESENT_DISPLAYONLY* pPresentDisplayOnly)
{
    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)hAdapter;
    NTSTATUS Status = STATUS_SUCCESS;

    LOGI("VidPnSourceId %d, pSource %p, BytesPerPixel %d, Pitch %d, Flags 0x%x, NumMoves %d, NumDirtyRects %d, pfn %p\n",
         pPresentDisplayOnly->VidPnSourceId,
         pPresentDisplayOnly->pSource,
         pPresentDisplayOnly->BytesPerPixel,
         pPresentDisplayOnly->Pitch,
         pPresentDisplayOnly->Flags.Value,
         pPresentDisplayOnly->NumMoves,
         pPresentDisplayOnly->NumDirtyRects,
         pPresentDisplayOnly->pfnPresentDisplayOnlyProgress);

    /*
     * Copy the image to the corresponding VidPn source allocation.
     */
    PVBOXWDDM_SOURCE pSource = &pDevExt->aSources[pPresentDisplayOnly->VidPnSourceId];
    VBOXVIDEOOFFSET const offVRAM = vboxWddmAddrVRAMOffset(&pSource->AllocData.Addr);

    if (pSource->bVisible) /// @todo Does/should this have any effect?
    {
        RECT updrect = { 0 };
        if (!FindUpdateRect(pPresentDisplayOnly->NumMoves,
                            pPresentDisplayOnly->pMoves,
                            pPresentDisplayOnly->NumDirtyRects,
                            pPresentDisplayOnly->pDirtyRect,
                            &updrect)) {
            updrect.top = 0;
            updrect.left = 0;
            updrect.bottom = pSource->AllocData.SurfDesc.height;
            updrect.right = pSource->AllocData.SurfDesc.width;
        }
        vboxWddmRectCopy(pDevExt->pvVisibleVram + offVRAM,    // dst pointer
            pSource->AllocData.SurfDesc.bpp / 8,              // dst bytes per pixel
            pSource->AllocData.SurfDesc.pitch,                // dst pitch
            pPresentDisplayOnly->pSource,                     // src pointer
            pPresentDisplayOnly->BytesPerPixel,               // src bytes per pixel
            pPresentDisplayOnly->Pitch,                       // src pitch
            &updrect);
        Status = gaSourceBlitToScreen(pDevExt, pSource, &updrect);
    }

    return Status;
}

NTSTATUS APIENTRY GaDxgkDdiPatch(const HANDLE hAdapter, const DXGKARG_PATCH *pPatch)
{
    LOGI("ENTER==> hAdapter(0x%p)\n", hAdapter);

    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)hAdapter;
    SvgaFlush(pDevExt->pGa->hw.pSvga);

    LOGI("pDmaBuffer %p, cbDmaBuffer %u, PatchStart %u, cPatches %u, Flags 0x%x, context type %d\n",
           pPatch->pDmaBuffer, pPatch->DmaBufferSubmissionEndOffset - pPatch->DmaBufferSubmissionStartOffset,
           pPatch->PatchLocationListSubmissionStart, pPatch->PatchLocationListSubmissionLength, pPatch->Flags.Value,
           ((PVBOXWDDM_CONTEXT)pPatch->hContext)->enmType);

    /* The driver does not need to modify paging commands. */
    if (pPatch->Flags.Paging)
        return STATUS_SUCCESS;

#ifdef VBOX_WITH_VMSVGA3D_DX
    PVBOXWDDM_CONTEXT pContext = (PVBOXWDDM_CONTEXT)pPatch->hContext;
    if (pContext->enmType == VBOXWDDM_CONTEXT_TYPE_VMSVGA_D3D)
        return DxgkDdiDXPatch(pDevExt, pPatch);
#endif

    UINT8 *pu8DMABuffer = (UINT8 *)pPatch->pDmaBuffer + pPatch->DmaBufferSubmissionStartOffset;
    UINT const cbDMABuffer = pPatch->DmaBufferSubmissionEndOffset - pPatch->DmaBufferSubmissionStartOffset;

    UINT i;
    for (i = pPatch->PatchLocationListSubmissionStart; i < pPatch->PatchLocationListSubmissionLength; ++i) {
        D3DDDI_PATCHLOCATIONLIST const *pPatchList = &pPatch->pPatchLocationList[i];
        DXGK_ALLOCATIONLIST const *pAllocationList = &pPatch->pAllocationList[pPatchList->AllocationIndex];
        if (pAllocationList->SegmentId == 0) {
            LOGE("no segment id specified");
            continue;
        }

        if (pPatchList->PatchOffset == ~0UL) {
            /* This is a dummy patch request, ignore. */
            continue;
        }

        if (pPatchList->PatchOffset >= cbDMABuffer) {
            LOGE("pPatchList->PatchOffset(%d) >= cbDMABuffer(%u)",
                 pPatchList->PatchOffset, cbDMABuffer);
            return STATUS_INVALID_PARAMETER;
        }

        UINT32 *poffVRAM = (UINT32 *)(pu8DMABuffer + pPatchList->PatchOffset);
        *poffVRAM = pAllocationList->PhysicalAddress.LowPart + pPatchList->AllocationOffset;
    }
    LOGI("LEAVE<== hAdapter(0x%p)\n", hAdapter);
    return STATUS_SUCCESS;
}

DECLINLINE(PVBOXWDDM_ALLOCATION) vboxWddmGetAllocationFromAllocList(
    DXGK_ALLOCATIONLIST *pAllocList)
{
    PVBOXWDDM_OPENALLOCATION pOa = (PVBOXWDDM_OPENALLOCATION)
                                    pAllocList->hDeviceSpecificAllocation;
    return pOa ? pOa->pAllocation : NULL;
}

#define GARENDERDATA_TYPE_RENDER    1
#define GARENDERDATA_TYPE_PRESENT   2
#define GARENDERDATA_TYPE_PAGING    3
#define GARENDERDATA_TYPE_FENCE     4

#define GA_DMA_MIN_SUBMIT_SIZE      4

static NTSTATUS gaGMRFBToVRAMSurface(DXGKARG_PRESENT *pPresent,
                                     PVBOXWDDM_EXT_VMSVGA pSvga,
                                     UINT32 idxAllocation,
                                     DXGK_ALLOCATIONLIST *pAllocationList,
                                     PVBOXWDDM_ALLOCATION pAllocation,
                                     UINT8 *pu8Target, UINT32 cbTarget, UINT32 *pu32TargetOut)
{
    NTSTATUS Status = SvgaGenDefineGMRFB(pSvga,
                                         pAllocationList->SegmentId != 0 ?
                                         pAllocationList->PhysicalAddress.LowPart : 0,
                                         pAllocation->AllocData.SurfDesc.pitch,
                                         pu8Target, cbTarget, pu32TargetOut);
    if (Status == STATUS_SUCCESS) {
        /* Always tell WDDM that the SHADOWSURFACE must be "paged in". */
        UINT PatchOffset =   (UINT)((uintptr_t)pu8Target - (uintptr_t)pPresent->pDmaBuffer)
                           + sizeof(UINT32)
                           + RT_UOFFSETOF(SVGAFifoCmdDefineGMRFB, ptr.offset);

        memset(pPresent->pPatchLocationListOut, 0, sizeof(D3DDDI_PATCHLOCATIONLIST));
        pPresent->pPatchLocationListOut->AllocationIndex = idxAllocation;
        pPresent->pPatchLocationListOut->PatchOffset = PatchOffset;
        ++pPresent->pPatchLocationListOut;
    }
    return Status;
}

/** Generate commands for Blt case.
 *
 * @return Status code.
 * @param pPresent          Information about rectangles to blit.
 * @param pSvga             VMSVGA device extension.
 * @param pSrc              Source allocation description.
 * @param pSrcAlloc         Allocation to blit from.
 * @param pDst              Destination allocation description.
 * @param pDstAlloc         Allocation to blit to.
 * @param pu8Target         Command buffer to fill.
 * @param cbTarget          Size of command buffer.
 * @param pu32TargetOut     Where to store size of generated commands.
 */
static NTSTATUS gaPresentBlt(DXGKARG_PRESENT *pPresent,
                             PVBOXWDDM_EXT_VMSVGA pSvga,
                             DXGK_ALLOCATIONLIST *pSrc,
                             PVBOXWDDM_ALLOCATION pSrcAlloc,
                             DXGK_ALLOCATIONLIST *pDst,
                             PVBOXWDDM_ALLOCATION pDstAlloc,
                             UINT8 *pu8Target, UINT32 cbTarget, UINT32 *pu32TargetOut)
{
    NTSTATUS Status = STATUS_SUCCESS;

    UINT8 * const pu8TargetStart = pu8Target;

    UINT32 cbCmd = 0;

    /** @todo One subrect at a time for now, consider passing all pDstSubRects when possible,
     * for example in one BlitSurfaceToScreen.
     */
    UINT32 iSubRect;
    for (iSubRect = pPresent->MultipassOffset; iSubRect < pPresent->SubRectCnt; ++iSubRect) {
        /* DstSubRects are in Dst coords.
         * To calculate corresponding SrcSubRect:
         *    srcsub = src + (dstsub - dst) = dstsub + (src - dst).
         * Precompute the src - dst differences to use in the code below.
         */
        INT32 const dx = pPresent->SrcRect.left - pPresent->DstRect.left;
        INT32 const dy = pPresent->SrcRect.top  - pPresent->DstRect.top;

        if (iSubRect == 0) {
            if (pSrcAlloc->enmType == VBOXWDDM_ALLOC_TYPE_STD_SHADOWSURFACE ||
                pSrcAlloc->enmType == VBOXWDDM_ALLOC_TYPE_STD_STAGINGSURFACE) {
                /* Define GMRFB to point to the shadow/staging surface. */
                Status = gaGMRFBToVRAMSurface(pPresent, pSvga,
                                              DXGK_PRESENT_SOURCE_INDEX, pSrc, pSrcAlloc,
                                              pu8Target, cbTarget, &cbCmd);
            } else if (pDstAlloc->enmType == VBOXWDDM_ALLOC_TYPE_STD_SHADOWSURFACE ||
                       pDstAlloc->enmType == VBOXWDDM_ALLOC_TYPE_STD_STAGINGSURFACE) {
                /* Define GMRFB to point to the shadow/staging surface. */
                Status = gaGMRFBToVRAMSurface(pPresent, pSvga,
                                              DXGK_PRESENT_DESTINATION_INDEX, pDst, pDstAlloc,
                                              pu8Target, cbTarget, &cbCmd);
            }

            if (Status == STATUS_BUFFER_OVERFLOW) {
                Status = STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER;
                break;
            }

            pu8Target += cbCmd;
            cbTarget -= cbCmd;
        }

        if (pDstAlloc->enmType == VBOXWDDM_ALLOC_TYPE_STD_SHAREDPRIMARYSURFACE) {
            /* To screen. */
            if (pSrcAlloc->enmType == VBOXWDDM_ALLOC_TYPE_STD_SHADOWSURFACE ||
                pSrcAlloc->enmType == VBOXWDDM_ALLOC_TYPE_STD_STAGINGSURFACE) {
                /* From GDI software drawing surface. */
                LOGW("Blt: %s(%d) 0x%08X -> SHAREDPRIMARYSURFACE 0x%08X\n",
                    vboxWddmAllocTypeString(pSrcAlloc),
                    pSrcAlloc->enmType, pSrc->PhysicalAddress.LowPart,
                    pDst->PhysicalAddress.LowPart);

                INT32 const xSrc = pPresent->pDstSubRects[iSubRect].left + dx;
                INT32 const ySrc = pPresent->pDstSubRects[iSubRect].top  + dy;
                Status = SvgaGenBlitGMRFBToScreen(pSvga,
                                                  pDstAlloc->AllocData.SurfDesc.VidPnSourceId,
                                                  xSrc, ySrc,
                                                  &pPresent->pDstSubRects[iSubRect],
                                                  pu8Target, cbTarget, &cbCmd);
            } else if (pSrcAlloc->enmType == VBOXWDDM_ALLOC_TYPE_UMD_RC_GENERIC
#ifdef VBOX_WITH_VMSVGA3D_DX
                     || pSrcAlloc->enmType == VBOXWDDM_ALLOC_TYPE_D3D
#endif
                    )
            {
                /* From a surface. */
#ifdef VBOX_WITH_VMSVGA3D_DX
                uint32_t const sid = pSrcAlloc->enmType == VBOXWDDM_ALLOC_TYPE_D3D
                                   ? pSrcAlloc->dx.sid
                                   : pSrcAlloc->AllocData.hostID;
#else
                uint32_t const sid = pSrcAlloc->AllocData.hostID;
#endif
                LOGI("Blt: surface sid=%u -> SHAREDPRIMARYSURFACE 0x%08X\n",
                      sid, pDst->PhysicalAddress.LowPart);

                RECT const dstRect = pPresent->pDstSubRects[iSubRect];
                RECT srcRect;
                srcRect.left   = dstRect.left   + dx;
                srcRect.top    = dstRect.top    + dy;
                srcRect.right  = dstRect.right  + dx;
                srcRect.bottom = dstRect.bottom + dy;
                RECT clipRect = dstRect;
                Status = SvgaGenBlitSurfaceToScreen(pSvga,
                                                    sid,
                                                    &srcRect,
                                                    pDstAlloc->AllocData.SurfDesc.VidPnSourceId,
                                                    &dstRect,
                                                    1, &clipRect,
                                                    pu8Target, cbTarget, &cbCmd, NULL);
            } else {
                LOGE("invalid enmType\n");
            }
        } else if (pDstAlloc->enmType == VBOXWDDM_ALLOC_TYPE_STD_SHADOWSURFACE ||
                   pDstAlloc->enmType == VBOXWDDM_ALLOC_TYPE_STD_STAGINGSURFACE) {
            /* To GDI software drawing surface. */
            if (pSrcAlloc->enmType == VBOXWDDM_ALLOC_TYPE_STD_SHAREDPRIMARYSURFACE) {
                /* From screen. */
                LOGI("Blt: SHAREDPRIMARYSURFACE 0x%08X -> %s(%d) 0x%08X\n",
                    pSrc->PhysicalAddress.LowPart,
                    vboxWddmAllocTypeString(pDstAlloc),
                    pDstAlloc->enmType, pDst->PhysicalAddress.LowPart);

                INT32 const xSrc = pPresent->pDstSubRects[iSubRect].left + dx;
                INT32 const ySrc = pPresent->pDstSubRects[iSubRect].top + dy;

                Status = SvgaGenBlitScreenToGMRFB(pSvga,
                                                  pSrcAlloc->AllocData.SurfDesc.VidPnSourceId,
                                                  xSrc, ySrc,
                                                  &pPresent->pDstSubRects[iSubRect],
                                                  pu8Target, cbTarget, &cbCmd);
            } else if (pSrcAlloc->enmType == VBOXWDDM_ALLOC_TYPE_UMD_RC_GENERIC
#ifdef VBOX_WITH_VMSVGA3D_DX
                     || pSrcAlloc->enmType == VBOXWDDM_ALLOC_TYPE_D3D
#endif
                    )
            {
                /* From a surface. */
#ifdef VBOX_WITH_VMSVGA3D_DX
                uint32_t const sid = pSrcAlloc->enmType == VBOXWDDM_ALLOC_TYPE_D3D
                                   ? pSrcAlloc->dx.sid
                                   : pSrcAlloc->AllocData.hostID;
#else
                uint32_t const sid = pSrcAlloc->AllocData.hostID;
#endif
                LOGI("Blt: surface sid=%u -> %s(%d) %d:0x%08X\n",
                    sid,
                    vboxWddmAllocTypeString(pDstAlloc),
                    pDstAlloc->enmType, pDst->SegmentId, pDst->PhysicalAddress.LowPart);

                SVGAGuestImage guestImage;
                guestImage.ptr.gmrId  = SVGA_GMR_FRAMEBUFFER;
                guestImage.ptr.offset = pDst->SegmentId != 0 ?  pDst->PhysicalAddress.LowPart : 0;
                guestImage.pitch      = pDstAlloc->AllocData.SurfDesc.pitch;

                SVGA3dSurfaceImageId surfId;
                surfId.sid    = sid;
                surfId.face   = 0;
                surfId.mipmap = 0;

                INT32 const xSrc = pPresent->pDstSubRects[iSubRect].left + dx;
                INT32 const ySrc = pPresent->pDstSubRects[iSubRect].top + dy;

                Status = SvgaGenSurfaceDMA(pSvga,
                                           &guestImage, &surfId, SVGA3D_READ_HOST_VRAM,
                                           xSrc, ySrc,
                                           pPresent->pDstSubRects[iSubRect].left,
                                           pPresent->pDstSubRects[iSubRect].top,
                                           pPresent->pDstSubRects[iSubRect].right - pPresent->pDstSubRects[iSubRect].left,
                                           pPresent->pDstSubRects[iSubRect].bottom - pPresent->pDstSubRects[iSubRect].top,
                                           pu8Target, cbTarget, &cbCmd);
                if (Status == STATUS_SUCCESS) {
                    /* Always tell WDDM that the SHADOWSURFACE must be "paged in". */
                    UINT PatchOffset = (UINT)((uintptr_t)pu8Target - (uintptr_t)pPresent->pDmaBuffer)
                                       + sizeof(SVGA3dCmdHeader)
                                       + RT_UOFFSETOF(SVGA3dCmdSurfaceDMA, guest.ptr.offset);

                    memset(pPresent->pPatchLocationListOut, 0, sizeof(D3DDDI_PATCHLOCATIONLIST));
                    pPresent->pPatchLocationListOut->AllocationIndex = DXGK_PRESENT_DESTINATION_INDEX;
                    pPresent->pPatchLocationListOut->PatchOffset = PatchOffset;
                    ++pPresent->pPatchLocationListOut;
                }
            } else {
                LOGE("invalid enmType\n");
            }
        } else if (pDstAlloc->enmType == VBOXWDDM_ALLOC_TYPE_UMD_RC_GENERIC &&
                   pSrcAlloc->enmType == VBOXWDDM_ALLOC_TYPE_UMD_RC_GENERIC) {
                    // both src and dst resources are from application, copy src surface to dst surface
                    uint32_t const srcSid = pSrcAlloc->AllocData.hostID;
                    uint32_t const dstSid = pDstAlloc->AllocData.hostID;

                    SVGA3dSurfaceImageId srcId;
                    srcId.sid    = srcSid;
                    srcId.face   = 0;
                    srcId.mipmap = 0;

                    SVGA3dSurfaceImageId dstId;
                    dstId.sid    = dstSid;
                    dstId.face   = 0;
                    dstId.mipmap = 0;

                    INT32 const xSrc = pPresent->pDstSubRects[iSubRect].left + dx;
                    INT32 const ySrc = pPresent->pDstSubRects[iSubRect].top + dy;

                    Status = SvgaGenSurfaceCopy(pSvga,
                                                &srcId, &dstId,xSrc, ySrc,
                                                pPresent->pDstSubRects[iSubRect].left,
                                                pPresent->pDstSubRects[iSubRect].top,
                                                pPresent->pDstSubRects[iSubRect].right - pPresent->pDstSubRects[iSubRect].left,
                                                pPresent->pDstSubRects[iSubRect].bottom - pPresent->pDstSubRects[iSubRect].top,
                                                pu8Target, cbTarget, &cbCmd);
        } else {
            LOGE("invalid enmType\n");
        }

        if (Status == STATUS_BUFFER_OVERFLOW) {
            Status = STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER;
            break;
        }

        pu8Target += cbCmd;
        cbTarget -= cbCmd;
    }

    *pu32TargetOut = (UINT)((uintptr_t)pu8Target - (uintptr_t)pu8TargetStart);

    if (Status == STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER) {
        pPresent->MultipassOffset = iSubRect;
    }

    return Status;
}

static NTSTATUS GaDxgkPresentBlt(DXGKARG_PRESENT *pPresent, PVBOXMP_DEVEXT pDevExt,
                             DXGK_ALLOCATIONLIST *pSrc, DXGK_ALLOCATIONLIST *pDst)
{
    NTSTATUS Status = STATUS_SUCCESS;

    PVBOXWDDM_ALLOCATION pSrcAlloc = vboxWddmGetAllocationFromAllocList(pSrc);
    PVBOXWDDM_ALLOCATION pDstAlloc = vboxWddmGetAllocationFromAllocList(pDst);

    LOGI("Blt: sid=%x -> sid=%x\n", pSrcAlloc->AllocData.hostID,
          pDstAlloc->AllocData.hostID);

    /** @todo Review standard allocations (DxgkDdiGetStandardAllocationDriverData, etc).
     *        Probably can be used more naturally with VMSVGA.
     */
    /** @todo Merge common code for all branches (Blt, Flip, ColorFill) */
    /* Generate DMA buffer containing the SVGA_CMD_BLIT_GMRFB_TO_SCREEN commands.
     * Store the command buffer descriptor to pDmaBufferPrivateData.
     */
    GARENDERDATA *pRenderData = NULL;
    UINT32 u32TargetLength = 0;
    UINT32 cbPrivateData = 0;

    if (pPresent->DmaBufferPrivateDataSize >= sizeof(GARENDERDATA)) {
        UINT8 *pu8Target = (UINT8 *)pPresent->pDmaBuffer;
        UINT32 cbTarget = pPresent->DmaSize;

        Status = gaPresentBlt(pPresent, pDevExt->pGa->hw.pSvga, pSrc,
                              pSrcAlloc, pDst, pDstAlloc,
                              pu8Target, cbTarget, &u32TargetLength);

        /* Fill RenderData description in any case, it will be ignored if the above code failed. */
        pRenderData = (GARENDERDATA *)pPresent->pDmaBufferPrivateData;
        pRenderData->u32DataType  = GARENDERDATA_TYPE_PRESENT;
        pRenderData->cbData       = u32TargetLength;
        pRenderData->pFenceObject = NULL; /* Not a user request, so no user accessible fence object. */
        pRenderData->pvDmaBuffer  = pPresent->pDmaBuffer;
        pRenderData->pHwRenderData = NULL;
        cbPrivateData = sizeof(GARENDERDATA);
    } else {
        LOGE("invalid dma buffer size\n");
        Status = STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER;
    }

    switch (Status) {
        case STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER:
        {
            if (pRenderData == NULL) {
                /* Not enough space in pDmaBufferPrivateData. */
                break;
            }
            RT_FALL_THRU();
        }
        case STATUS_SUCCESS:
        {
            pPresent->pDmaBuffer = (UINT8 *)pPresent->pDmaBuffer + u32TargetLength;
            pPresent->pDmaBufferPrivateData = (UINT8 *)pPresent->pDmaBufferPrivateData + cbPrivateData;
            break;
        }
        default:
            break;
    }

    return Status;
}

static NTSTATUS GaDxgkPresentFlip(DXGKARG_PRESENT *pPresent, PVBOXMP_DEVEXT pDevExt,
                             DXGK_ALLOCATIONLIST *pSrc, DXGK_ALLOCATIONLIST *pDst)
{
    NTSTATUS Status = STATUS_SUCCESS;

    RT_NOREF(pDst);

    PVBOXWDDM_ALLOCATION pSrcAlloc = vboxWddmGetAllocationFromAllocList(pSrc);

    UINT32 sid;
    UINT32 width;
    UINT32 height;
    UINT32 VidPnSourceId;

    if (pSrcAlloc->enmType == VBOXWDDM_ALLOC_TYPE_UMD_RC_GENERIC)
    {
        sid           = pSrcAlloc->AllocData.hostID;
        width         = pSrcAlloc->AllocData.SurfDesc.width;
        height        = pSrcAlloc->AllocData.SurfDesc.height;
        VidPnSourceId = pSrcAlloc->AllocData.SurfDesc.VidPnSourceId;
    }
#ifdef VBOX_WITH_VMSVGA3D_DX
    else if (pSrcAlloc->enmType == VBOXWDDM_ALLOC_TYPE_D3D)
    {
        Assert(pSrcAlloc->dx.desc.fPrimary);
        sid           = pSrcAlloc->dx.sid;
        width         = pSrcAlloc->dx.desc.surfaceInfo.size.width;
        height        = pSrcAlloc->dx.desc.surfaceInfo.size.height;
        VidPnSourceId = pSrcAlloc->dx.desc.PrimaryDesc.VidPnSourceId;
    }
#endif
    else
        return STATUS_INVALID_PARAMETER;

    LOGI("Flip: %s sid=%u %dx%d\n",
         vboxWddmAllocTypeString(pSrcAlloc), sid, width, height);

    /* Generate DMA buffer containing the present commands.
     * Store the command buffer descriptor to pDmaBufferPrivateData.
     */
    GARENDERDATA *pRenderData = NULL;
    UINT32 u32TargetLength = 0;
    UINT32 cbPrivateData = 0;

    if (pPresent->DmaBufferPrivateDataSize >= sizeof(GARENDERDATA)) {
        void *pvTarget          = pPresent->pDmaBuffer;
        const UINT32 cbTarget   = pPresent->DmaSize;
        if (cbTarget > GA_DMA_MIN_SUBMIT_SIZE) {
            /* SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN */
            RECT rect;
            rect.left   = 0;
            rect.top    = 0;
            rect.right  = width;
            rect.bottom = height;
            UINT32 const cInClipRects = pPresent->SubRectCnt - pPresent->MultipassOffset;
            UINT32 cOutClipRects = 0;
            Status = SvgaGenBlitSurfaceToScreen(pDevExt->pGa->hw.pSvga,
                                                sid,
                                                &rect,
                                                VidPnSourceId,
                                                &rect,
                                                cInClipRects,
                                                pPresent->pDstSubRects + pPresent->MultipassOffset,
                                                pvTarget, cbTarget, &u32TargetLength, &cOutClipRects);
            if (Status == STATUS_BUFFER_OVERFLOW) {
                Status = STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER;
            }

            if (Status == STATUS_SUCCESS) {
                if (cOutClipRects < cInClipRects) {
                    /* Not all rectangles were copied. */
                    Status = STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER;
                }
                /* Advance the current rectangle index. */
                pPresent->MultipassOffset += cOutClipRects;
            }
        } else {
            LOGE("invalid dma buffer size\n");
            Status = STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER;
        }

        /* Fill RenderData description in any case, it will be ignored if the above code failed. */
        pRenderData = (GARENDERDATA *)pPresent->pDmaBufferPrivateData;
        pRenderData->u32DataType  = GARENDERDATA_TYPE_PRESENT;
        pRenderData->cbData       = u32TargetLength;
        pRenderData->pFenceObject = NULL; /* Not a user request, so no user accessible fence object. */
        pRenderData->pvDmaBuffer = pPresent->pDmaBuffer;
        pRenderData->pHwRenderData = NULL;
        cbPrivateData = sizeof(GARENDERDATA);
    } else {
        Status = STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER;
    }

    switch (Status) {
        case STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER:
        {
            if (pRenderData == NULL)
            {
                /* Not enough space in pDmaBufferPrivateData. */
                break;
            }
            RT_FALL_THRU();
        }
        case STATUS_SUCCESS:
        {
            pPresent->pDmaBuffer = (UINT8 *)pPresent->pDmaBuffer + u32TargetLength;
            pPresent->pDmaBufferPrivateData = (UINT8 *)pPresent->pDmaBufferPrivateData + cbPrivateData;
            break;
        }
        default: break;
    }

    return Status;
}

static NTSTATUS GaDxgkPresentColorFill(DXGKARG_PRESENT *pPresent, PVBOXMP_DEVEXT pDevExt,
                             DXGK_ALLOCATIONLIST *pSrc, DXGK_ALLOCATIONLIST *pDst)
{
    RT_NOREF(pPresent, pDevExt, pSrc, pDst);
    LOGE("ColorFill is not implemented\n");
    return STATUS_NOT_SUPPORTED;
}

NTSTATUS APIENTRY GaDxgkDdiPresent(const HANDLE hContext,
                                   DXGKARG_PRESENT *pPresent)
{
    LOGI("ENTER==> hContext(0x%p)\n", hContext);
    NTSTATUS Status = STATUS_SUCCESS;
    PVBOXWDDM_CONTEXT pContext = (PVBOXWDDM_CONTEXT)hContext;
    PVBOXWDDM_DEVICE pDevice = pContext->pDevice;
    PVBOXMP_DEVEXT pDevExt = pDevice->pAdapter;

    SvgaFlush(pDevExt->pGa->hw.pSvga);

    INT64 ttt = SystemTime();
    LOGI("GaDxgkDdiPresent time=%lld, flag=0x%08x\n",
          ttt, pPresent->Flags);
    LOGI("DXGK PRESENT SOURCE INDEX=%u, DEST INDEX=%u\n",
         DXGK_PRESENT_SOURCE_INDEX, DXGK_PRESENT_DESTINATION_INDEX);
    DXGK_ALLOCATIONLIST *pSrc = &pPresent->pAllocationList[DXGK_PRESENT_SOURCE_INDEX];
    DXGK_ALLOCATIONLIST *pDst = &pPresent->pAllocationList[DXGK_PRESENT_DESTINATION_INDEX];

    LOGI("%s: [%ld, %ld, %ld, %ld] -> [%ld, %ld, %ld, %ld] (SubRectCnt=%u)\n",
        pPresent->Flags.Blt ? "Blt" : (pPresent->Flags.Flip ? "Flip" : (pPresent->Flags.ColorFill ? "ColorFill" : "Unknown OP")),
        pPresent->SrcRect.left, pPresent->SrcRect.top, pPresent->SrcRect.right, pPresent->SrcRect.bottom,
        pPresent->DstRect.left, pPresent->DstRect.top, pPresent->DstRect.right, pPresent->DstRect.bottom,
        pPresent->SubRectCnt);
    for (UINT i = 0; i < pPresent->SubRectCnt; ++i) {
        LOGI("   sub#%u = [%ld, %ld, %ld, %ld]\n",
             i, pPresent->pDstSubRects[i].left, pPresent->pDstSubRects[i].top,
             pPresent->pDstSubRects[i].right, pPresent->pDstSubRects[i].bottom);
    }

    if (pPresent->Flags.Blt) {
        Status = GaDxgkPresentBlt(pPresent, pDevExt, pSrc, pDst);
    } else if (pPresent->Flags.Flip) {
        Status = GaDxgkPresentFlip(pPresent, pDevExt, pSrc, pDst);
    } else if (pPresent->Flags.ColorFill) {
        Status = GaDxgkPresentColorFill(pPresent, pDevExt, pSrc, pDst);
    } else {
        LOGE("cmd NOT IMPLEMENTED!! Flags(0x%x)", pPresent->Flags.Value);
        Status = STATUS_NOT_SUPPORTED;
    }

    LOGI("LEAVE<== hContext(0x%p), Status 0x%x\n", hContext, Status);
    return Status;
}

NTSTATUS APIENTRY GaDxgkDdiRender(const HANDLE hContext, DXGKARG_RENDER *pRender)
{
    LOGI("ENTER==> hContext(0x%p)\n", hContext);
    PVBOXWDDM_CONTEXT pContext = (PVBOXWDDM_CONTEXT)hContext;
    PVBOXWDDM_DEVICE pDevice = pContext->pDevice;
    PVBOXMP_DEVEXT pDevExt = pDevice->pAdapter;
    VBOXWDDM_EXT_GA *pGaDevExt = pDevExt->pGa;

    SvgaFlush(pDevExt->pGa->hw.pSvga);

#ifdef VBOX_WITH_VMSVGA3D_DX
    if (pContext->enmType == VBOXWDDM_CONTEXT_TYPE_VMSVGA_D3D)
        return DxgkDdiDXRender(pContext, pRender);
#endif

    AssertReturn(pContext && pContext->enmType == VBOXWDDM_CONTEXT_TYPE_GA_3D, STATUS_INVALID_PARAMETER);
    AssertReturn(pRender->CommandLength > pRender->MultipassOffset, STATUS_INVALID_PARAMETER);
    /* Expect 32 bit handle at the start of the command buffer. */
    AssertReturn(pRender->CommandLength >= sizeof(UINT32), STATUS_INVALID_PARAMETER);

    GARENDERDATA *pRenderData = NULL;  /* Pointer to the DMA buffer description. */
    UINT32 cbPrivateData = 0;        /* Bytes to place into the private data buffer. */
    UINT32 u32TargetLength = 0;      /* Bytes to place into the DMA buffer. */
    UINT32 u32ProcessedLength = 0;   /* Bytes consumed from command buffer. */

    LOGI("[%p] Command %p/%d, Dma %p/%d, Private %p/%d, MO %d, S %d, Phys 0x%lx, AL %p/%d, PLLIn %p/%d, PLLOut %p/%d\n",
           hContext,
           pRender->pCommand, pRender->CommandLength,
           pRender->pDmaBuffer, pRender->DmaSize,
           pRender->pDmaBufferPrivateData, pRender->DmaBufferPrivateDataSize,
           pRender->MultipassOffset, pRender->DmaBufferSegmentId, pRender->DmaBufferPhysicalAddress.QuadPart,
           pRender->pAllocationList, pRender->AllocationListSize,
           pRender->pPatchLocationListIn, pRender->PatchLocationListInSize,
           pRender->pPatchLocationListOut, pRender->PatchLocationListOutSize
    );

    /* 32 bit handle at the start of the command buffer. */
    if (pRender->MultipassOffset == 0) {
        pRender->MultipassOffset += sizeof(UINT32);
    }

    NTSTATUS Status = STATUS_SUCCESS;
    __try
    {
        /* Calculate where the commands start. */
        void const *pvSource = (UINT8 *)pRender->pCommand + pRender->MultipassOffset;
        UINT32 cbSource = pRender->CommandLength - pRender->MultipassOffset;

        /* Generate DMA buffer from the supplied command buffer.
         * Store the command buffer descriptor to pDmaBufferPrivateData.
         *
         * The display miniport driver must validate the command buffer.
         *
         * Copy commands to the pDmaBuffer.
         * If a command uses a shared surface id, then replace the id with the original surface id.
         */
        if (pRender->DmaBufferPrivateDataSize >= sizeof(GARENDERDATA)) {
            void *pvTarget          = pRender->pDmaBuffer;
            UINT32 const cbTarget = pRender->DmaSize;
            GAHWRENDERDATA *pHwRenderData = NULL;
            if (cbTarget > GA_DMA_MIN_SUBMIT_SIZE) {
                Status = SvgaRenderCommands(pGaDevExt->hw.pSvga, pContext->pSvgaContext, pvTarget, cbTarget, pvSource, cbSource,
                                            &u32TargetLength, &u32ProcessedLength, &pHwRenderData);
            } else {
                LOGE("invalid dma buffer size %u\n", cbTarget);
                Status = STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER;
            }

            GAFENCEOBJECT *pFO = NULL;
            if (Status == STATUS_SUCCESS) {
                /* Completed the command buffer. Check if there is a user mode fence. */
                UINT32 const u32FenceHandle = *(UINT32 *)pRender->pCommand;
                if (u32FenceHandle != 0) {
                    /* Verify that the buffer handle is valid. */
                    gaFenceObjectsLock(pGaDevExt);
                    pFO = GaFenceLookup(pGaDevExt, u32FenceHandle);
                    gaFenceObjectsUnlock(pGaDevExt);
                    if (!pFO) { // Maybe silently ignore?
                        LOGE("GaFenceLookup failed\n");
                        Status = STATUS_INVALID_PARAMETER;
                    }
                }

                LOGI("u32FenceHandle = %d, pFO = %p\n", u32FenceHandle, pFO);
            }

            /* Fill RenderData description in any case, it will be ignored if the above code failed. */
            pRenderData = (GARENDERDATA *)pRender->pDmaBufferPrivateData;
            pRenderData->u32DataType  = GARENDERDATA_TYPE_RENDER;
            pRenderData->cbData       = u32TargetLength;
            pRenderData->pFenceObject = pFO;
            pRenderData->pvDmaBuffer  = pRender->pDmaBuffer;
            pRenderData->pHwRenderData = pHwRenderData;
            cbPrivateData = sizeof(GARENDERDATA);
        } else {
            LOGE("invliad dma buffer size\n");
            Status = STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER;
        }

        LOGI("Status = 0x%x\n", Status);
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        LOGE("exception happened!\n");
        Status = STATUS_INVALID_PARAMETER;
    }

    switch (Status) {
        case STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER:
        {
            pRender->MultipassOffset += u32ProcessedLength;
            if (pRenderData == NULL)
            {
                /* Not enough space in pDmaBufferPrivateData. */
                break;
            }
            RT_FALL_THRU();
        }
        case STATUS_SUCCESS:
        {
            Assert(pRenderData);
            if (u32TargetLength == 0) {
                /* Trigger command submission anyway by increasing pRender->pDmaBufferPrivateData */
                /* Update the DMA buffer description. */
                pRenderData->u32DataType  = GARENDERDATA_TYPE_FENCE;
                pRenderData->cbData       = 0;
                /* pRenderData->pFenceObject stays */
                pRenderData->pvDmaBuffer  = NULL; /* Not used */
            }
            pRender->pDmaBuffer = (UINT8 *)pRender->pDmaBuffer + u32TargetLength;
            pRender->pDmaBufferPrivateData = (UINT8 *)pRender->pDmaBufferPrivateData + cbPrivateData;
            break;
        }
        default: break;
    }
    LOGI("LEAVE<== hContext(0x%p)\n", hContext);
    return Status;
}

NTSTATUS APIENTRY GaDxgkDdiBuildPagingBuffer(const HANDLE hAdapter,
                                             DXGKARG_BUILDPAGINGBUFFER *pBuildPagingBuffer)
{
    RT_NOREF(hAdapter);
    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)hAdapter;
    SvgaFlush(pDevExt->pGa->hw.pSvga);

    NTSTATUS Status = STATUS_SUCCESS;

    LOGI("DmaBufferPrivateData %p/%d, DmaBuffer %p/%d\n",
         pBuildPagingBuffer->pDmaBufferPrivateData,
         pBuildPagingBuffer->DmaBufferPrivateDataSize,
         pBuildPagingBuffer->pDmaBuffer,
         pBuildPagingBuffer->DmaSize);

#ifdef VBOX_WITH_VMSVGA3D_DX
    /** @todo Old code did not generate any paging command actually. So probably one function is enough. */
    if (SvgaIsDXSupported(pDevExt)) {
        return DxgkDdiDXBuildPagingBuffer(pDevExt, pBuildPagingBuffer);
    }
#endif

    /* Generate DMA buffer containing the commands.
     * Store the command buffer descriptor pointer to pDmaBufferPrivateData.
     */
    GARENDERDATA *pRenderData = NULL;
    UINT32 u32TargetLength = 0;
    UINT32 cbPrivateData = 0;

    if (pBuildPagingBuffer->DmaBufferPrivateDataSize < sizeof(GARENDERDATA)) {
        Status = STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER;
        goto out0;
    }

    const UINT32 cbTarget = pBuildPagingBuffer->DmaSize;
    if (cbTarget <= GA_DMA_MIN_SUBMIT_SIZE) {
        Status = STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER;
        goto out1;
    }

    switch (pBuildPagingBuffer->Operation) {
        case DXGK_OPERATION_TRANSFER:
        {
            LOGI("DXGK_OPERATION_TRANSFER: %p: @0x%x, cb 0x%x; src: %d:%p; dst: %d:%p; flags 0x%x, off 0x%x\n",
                 pBuildPagingBuffer->Transfer.hAllocation,
                 pBuildPagingBuffer->Transfer.TransferOffset,
                 pBuildPagingBuffer->Transfer.TransferSize,
                 pBuildPagingBuffer->Transfer.Source.SegmentId,
                 pBuildPagingBuffer->Transfer.Source.pMdl,
                 pBuildPagingBuffer->Transfer.Destination.SegmentId,
                 pBuildPagingBuffer->Transfer.Destination.pMdl,
                 pBuildPagingBuffer->Transfer.Flags.Value,
                 pBuildPagingBuffer->Transfer.MdlOffset);
            if (pBuildPagingBuffer->Transfer.Source.SegmentId == 0) {
                /* SysMem source. */
                if (pBuildPagingBuffer->Transfer.Destination.SegmentId == 1) {
                    /* SysMem -> VRAM. */
                    /* Generate a NOP. */
                    Status = STATUS_NOT_SUPPORTED;
                } else if (pBuildPagingBuffer->Transfer.Destination.SegmentId == 0) {
                    /* SysMem -> SysMem, should not happen, bugcheck. */
                    LOGE("SysMem -> SysMem, should not happen\n");
                    Status = STATUS_INVALID_PARAMETER;
                } else {
                    /* SysMem -> GPU surface. Our driver probably does not need it.
                     * SVGA_3D_CMD_SURFACE_DMA(GMR -> Surface)?
                     */
                    LOGE("SysMem -> GPU surface, not supported\n");
                    Status = STATUS_NOT_SUPPORTED;
                }
            } else if (pBuildPagingBuffer->Transfer.Source.SegmentId == 1) {
                /* VRAM source. */
                if (pBuildPagingBuffer->Transfer.Destination.SegmentId == 0) {
                    /* VRAM -> SysMem. */
                    /* Generate a NOP. */
                    Status = STATUS_NOT_SUPPORTED;
                } else if (pBuildPagingBuffer->Transfer.Destination.SegmentId == 1) {
                    /* VRAM -> VRAM, should not happen, bugcheck. */
                    LOGE("VRAM -> VRAM, should not happen\n");
                    Status = STATUS_INVALID_PARAMETER;
                } else {
                    /* VRAM -> GPU surface. Our driver probably does not need it.
                     * SVGA_3D_CMD_SURFACE_DMA(SVGA_GMR_FRAMEBUFFER -> Surface)?
                     */
                    LOGE("VRAM -> GPU surface, not supported\n");
                    Status = STATUS_NOT_SUPPORTED;
                }
            } else {
                /* GPU surface. Our driver probably does not need it.
                 * SVGA_3D_CMD_SURFACE_DMA(Surface -> GMR)?
                 */
                LOGE("Source Segment for GPU surface, not supported\n");
                Status = STATUS_NOT_SUPPORTED;
            }

            /** @todo Ignore for now. */
            if (Status == STATUS_NOT_SUPPORTED) {
                /* NOP */
                Status = STATUS_SUCCESS;
            }
        } break;
        case DXGK_OPERATION_FILL:
        {
            LOGI("DXGK_OPERATION_FILL: %p: cb 0x%x, pattern 0x%x, %d:0x%08X\n",
                 pBuildPagingBuffer->Fill.hAllocation,
                 pBuildPagingBuffer->Fill.FillSize,
                 pBuildPagingBuffer->Fill.FillPattern,
                 pBuildPagingBuffer->Fill.Destination.SegmentId,
                 pBuildPagingBuffer->Fill.Destination.SegmentAddress.LowPart);
            /* NOP */
        } break;
        case DXGK_OPERATION_DISCARD_CONTENT:
        {
            LOGI("DXGK_OPERATION_DISCARD_CONTENT: %p: flags 0x%x, %d:0x%08X\n",
                 pBuildPagingBuffer->DiscardContent.hAllocation,
                 pBuildPagingBuffer->DiscardContent.Flags,
                 pBuildPagingBuffer->DiscardContent.SegmentId,
                 pBuildPagingBuffer->DiscardContent.SegmentAddress.LowPart);
            /* NOP */
        } break;
        default:
            LOGE("invalid operation\n");
            break;
    }

out1:
    /* Fill RenderData description in any case, it will be ignored if the above code failed. */
    pRenderData = (GARENDERDATA *)pBuildPagingBuffer->pDmaBufferPrivateData;
    pRenderData->u32DataType  = GARENDERDATA_TYPE_PAGING;
    pRenderData->cbData       = u32TargetLength;
    pRenderData->pFenceObject = NULL; /* Not a user request, so no user accessible fence object. */
    pRenderData->pvDmaBuffer = pBuildPagingBuffer->pDmaBuffer;
    pRenderData->pHwRenderData = NULL;
    cbPrivateData = sizeof(GARENDERDATA);

out0:
    switch (Status) {
        case STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER:
            LOGE("insufficient dma buffer\n"); /** @todo test */
            if (pRenderData == NULL) {
                /* Not enough space in pDmaBufferPrivateData. */
                break;
            }
            RT_FALL_THRU();
        case STATUS_SUCCESS:
        {
            pBuildPagingBuffer->pDmaBuffer =
                (UINT8 *)pBuildPagingBuffer->pDmaBuffer + u32TargetLength;
            pBuildPagingBuffer->pDmaBufferPrivateData =
                (UINT8 *)pBuildPagingBuffer->pDmaBufferPrivateData + cbPrivateData;
        } break;
        default: break;
    }

    return STATUS_SUCCESS;
}

static NTSTATUS gaSharedSidInsert(PVBOXWDDM_EXT_GA pGaDevExt,
                                  UINT32 u32Sid,
                                  UINT32 u32SharedSid)
{
    VBOXWDDM_EXT_VMSVGA *pSvga = pGaDevExt->hw.pSvga;
    return SvgaSharedSidInsert(pSvga, u32Sid, u32SharedSid);
}

static NTSTATUS gaSharedSidRemove(PVBOXWDDM_EXT_GA pGaDevExt,
                                  UINT32 u32Sid)
{
    VBOXWDDM_EXT_VMSVGA *pSvga = pGaDevExt->hw.pSvga;
    return SvgaSharedSidRemove(pSvga, u32Sid);
}

static NTSTATUS gaPresent(PVBOXWDDM_EXT_GA pGaDevExt,
                          UINT32 u32Sid,
                          UINT32 u32Width,
                          UINT32 u32Height,
                          UINT32 u32VRAMOffset)
{
    return SvgaPresentVRAM(pGaDevExt->hw.pSvga, u32Sid, u32Width, u32Height, u32VRAMOffset);
}

static NTSTATUS gaSurfaceDestroy(PVBOXWDDM_EXT_GA pGaDevExt,
                                 UINT32 u32Sid)
{
    VBOXWDDM_EXT_VMSVGA *pSvga = pGaDevExt->hw.pSvga;
    return SvgaSurfaceUnref(pSvga, u32Sid);
}

static NTSTATUS gaSurfaceDefine(PVBOXWDDM_EXT_GA pGaDevExt,
                                GASURFCREATE *pCreateParms,
                                GASURFSIZE *paSizes,
                                UINT32 cSizes,
                                UINT32 *pu32Sid)
{
    VBOXWDDM_EXT_VMSVGA *pSvga = pGaDevExt->hw.pSvga;
    return SvgaSurfaceCreate(pSvga, pCreateParms, paSizes, cSizes, pu32Sid);
}

NTSTATUS APIENTRY GaDxgkDdiEscape(const HANDLE hAdapter,
                                  const DXGKARG_ESCAPE *pEscape)
{
    NTSTATUS Status = STATUS_NOT_SUPPORTED;
    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)hAdapter;
    PVBOXWDDM_DEVICE pDevice = (PVBOXWDDM_DEVICE)pEscape->hDevice;
    PVBOXWDDM_CONTEXT pContext = (PVBOXWDDM_CONTEXT)pEscape->hContext;
    const VBOXDISPIFESCAPE *pEscapeHdr = (VBOXDISPIFESCAPE *)pEscape->pPrivateDriverData;

    switch (pEscapeHdr->escapeCode) {
    case VBOXESC_GAGETCID:
    {
        if (!pContext) {
            Status = STATUS_INVALID_PARAMETER;
            break;
        }

        if (pEscape->PrivateDriverDataSize < sizeof(VBOXDISPIFESCAPE_GAGETCID)) {
            Status = STATUS_INVALID_PARAMETER;
            break;
        }

        PVMSVGACONTEXT pSvgaContext = pContext->pSvgaContext;
        if (!pSvgaContext)
        {
            Status = STATUS_NOT_SUPPORTED;
            break;
        }

        VBOXDISPIFESCAPE_GAGETCID *pGaGetCid = (VBOXDISPIFESCAPE_GAGETCID *)pEscapeHdr;
        pGaGetCid->Cid = pSvgaContext->u32Cid;
        Status = STATUS_SUCCESS;
        break;
    }
    case VBOXESC_GAREGION:
    {
        if (pEscape->PrivateDriverDataSize < sizeof(VBOXDISPIFESCAPE_GAREGION)) {
            Status = STATUS_INVALID_PARAMETER;
            break;
        }

        VBOXDISPIFESCAPE_GAREGION *pGaRegion = (VBOXDISPIFESCAPE_GAREGION *)pEscapeHdr;
        if (pGaRegion->u32Command == GA_REGION_CMD_CREATE) {
            Status = SvgaRegionCreate(pDevExt->pGa->hw.pSvga, pDevice, pGaRegion->u32NumPages, &pGaRegion->u32GmrId, &pGaRegion->u64UserAddress);
            if (!NT_SUCCESS(Status)) {
                VBOXWDDM_EXT_VMSVGA *pSvga = pDevExt->pGa->hw.pSvga;
                LOGE("Region create escape failed: cAllocatedGmrs=%u, cAllocatedGmrPages=%u\n", pSvga->cAllocatedGmrs, pSvga->cAllocatedGmrPages);
            }
        } else if (pGaRegion->u32Command == GA_REGION_CMD_DESTROY) {
            Status = SvgaRegionDestroy(pDevExt->pGa->hw.pSvga, pGaRegion->u32GmrId);
        } else {
            Status = STATUS_INVALID_PARAMETER;
        }
        break;
    }
    case VBOXESC_GAPRESENT:
    {
        if (pEscape->PrivateDriverDataSize < sizeof(VBOXDISPIFESCAPE_GAPRESENT))
        {
            Status = STATUS_INVALID_PARAMETER;
            break;
        }

        VBOXDISPIFESCAPE_GAPRESENT *pGaPresent = (VBOXDISPIFESCAPE_GAPRESENT *)pEscapeHdr;
        /** @todo This always writes to the start of VRAM. This is a debug function
            * and is not used for normal operations anymore.
            */
        Status = gaPresent(pDevExt->pGa, pGaPresent->u32Sid, pGaPresent->u32Width, pGaPresent->u32Height, 0);
        break;
    }
    case VBOXESC_GASURFACEDEFINE:
    {
        LOGI("========================VBOXESC_GASURFACEDEFINE=====================\n");
        if (pEscape->PrivateDriverDataSize < sizeof(VBOXDISPIFESCAPE_GASURFACEDEFINE))
        {
            Status = STATUS_INVALID_PARAMETER;
            break;
        }

        VBOXDISPIFESCAPE_GASURFACEDEFINE *pGaSurfaceDefine = (VBOXDISPIFESCAPE_GASURFACEDEFINE *)pEscapeHdr;
        if (pEscape->PrivateDriverDataSize - sizeof(VBOXDISPIFESCAPE_GASURFACEDEFINE) < pGaSurfaceDefine->cbReq)
        {
            Status = STATUS_INVALID_PARAMETER;
            break;
        }

        if (pGaSurfaceDefine->cbReq < sizeof(GASURFCREATE))
        {
            Status = STATUS_INVALID_PARAMETER;
            break;
        }

        UINT32 const cSizes = (pGaSurfaceDefine->cbReq - sizeof(GASURFCREATE)) / sizeof(GASURFSIZE);
        if (cSizes != pGaSurfaceDefine->cSizes)
        {
            Status = STATUS_INVALID_PARAMETER;
            break;
        }

        GASURFCREATE *pCreateParms = (GASURFCREATE *)&pGaSurfaceDefine[1];
        GASURFSIZE *paSizes = (GASURFSIZE *)&pCreateParms[1];

        Status = gaSurfaceDefine(pDevExt->pGa, pCreateParms, paSizes, pGaSurfaceDefine->cSizes, &pGaSurfaceDefine->u32Sid);
        LOGI("----------------------------VBOXESC_GASURFACEDEFINE--------------------\n");
        break;
    }
    case VBOXESC_GASURFACEDESTROY:
    {
        if (pEscape->PrivateDriverDataSize < sizeof(VBOXDISPIFESCAPE_GASURFACEDESTROY))
        {
            Status = STATUS_INVALID_PARAMETER;
            break;
        }

        VBOXDISPIFESCAPE_GASURFACEDESTROY *pGaSurfaceDestroy = (VBOXDISPIFESCAPE_GASURFACEDESTROY *)pEscapeHdr;
        Status = gaSurfaceDestroy(pDevExt->pGa, pGaSurfaceDestroy->u32Sid);
        break;
    }
    case VBOXESC_GASHAREDSID:
    {
        if (pEscape->PrivateDriverDataSize < sizeof(VBOXDISPIFESCAPE_GASHAREDSID))
        {
            Status = STATUS_INVALID_PARAMETER;
            break;
        }

        VBOXDISPIFESCAPE_GASHAREDSID *pGaSharedSid = (VBOXDISPIFESCAPE_GASHAREDSID *)pEscapeHdr;
        if (pGaSharedSid->u32SharedSid == ~0)
        {
            Status = gaSharedSidRemove(pDevExt->pGa, pGaSharedSid->u32Sid);
        }
        else
        {
            Status = gaSharedSidInsert(pDevExt->pGa, pGaSharedSid->u32Sid, pGaSharedSid->u32SharedSid);
        }
        break;
    }
    case VBOXESC_GAFENCECREATE:
    {
        if (!pDevice)
        {
            Status = STATUS_INVALID_PARAMETER;
            break;
        }

        if (pEscape->PrivateDriverDataSize < sizeof(VBOXDISPIFESCAPE_GAFENCECREATE))
        {
            Status = STATUS_INVALID_PARAMETER;
            break;
        }

        VBOXDISPIFESCAPE_GAFENCECREATE *pFenceCreate = (VBOXDISPIFESCAPE_GAFENCECREATE *)pEscapeHdr;
        Status = GaFenceCreate(pDevExt->pGa, pDevice, &pFenceCreate->u32FenceHandle);
        break;
    }
    case VBOXESC_GAFENCEQUERY:
    {
        if (pEscape->PrivateDriverDataSize < sizeof(VBOXDISPIFESCAPE_GAFENCEQUERY))
        {
            Status = STATUS_INVALID_PARAMETER;
            break;
        }

        VBOXDISPIFESCAPE_GAFENCEQUERY *pFenceQuery = (VBOXDISPIFESCAPE_GAFENCEQUERY *)pEscapeHdr;
        Status = GaFenceQuery(pDevExt->pGa, pFenceQuery->u32FenceHandle,
                              &pFenceQuery->u32SubmittedSeqNo, &pFenceQuery->u32ProcessedSeqNo,
                              &pFenceQuery->u32FenceStatus);
        break;
    }
    case VBOXESC_GAFENCEWAIT:
    {
        if (pEscape->PrivateDriverDataSize < sizeof(VBOXDISPIFESCAPE_GAFENCEWAIT))
        {
            Status = STATUS_INVALID_PARAMETER;
            break;
        }

        VBOXDISPIFESCAPE_GAFENCEWAIT *pFenceWait = (VBOXDISPIFESCAPE_GAFENCEWAIT *)pEscapeHdr;
        Status = GaFenceWait(pDevExt->pGa, pFenceWait->u32FenceHandle, pFenceWait->u64TimeoutNS);
        break;
    }
    case VBOXESC_GAFENCEUNREF:
    {
        if (pEscape->PrivateDriverDataSize < sizeof(VBOXDISPIFESCAPE_GAFENCEUNREF))
        {
            Status = STATUS_INVALID_PARAMETER;
            break;
        }

        VBOXDISPIFESCAPE_GAFENCEUNREF *pFenceUnref = (VBOXDISPIFESCAPE_GAFENCEUNREF *)pEscapeHdr;
        Status = GaFenceDelete(pDevExt->pGa, pFenceUnref->u32FenceHandle);
        break;
    }
    case VBOXESC_SVGAGBSURFACEDEFINE:
    {
        if (pEscape->PrivateDriverDataSize < sizeof(VBOXDISPIFESCAPE_SVGAGBSURFACEDEFINE))
        {
            Status = STATUS_INVALID_PARAMETER;
            break;
        }

        VBOXDISPIFESCAPE_SVGAGBSURFACEDEFINE *pSurfaceDefine = (VBOXDISPIFESCAPE_SVGAGBSURFACEDEFINE *)pEscapeHdr;

        VBOXWDDM_EXT_VMSVGA *pSvga = pDevExt->pGa->hw.pSvga;
        Status = SvgaGBSurfaceCreate(pSvga, pDevice, &pSurfaceDefine->CreateParms);
        break;
    }
    case VBOXESC_SVGAGETSID:
    {
        if (pEscape->PrivateDriverDataSize < sizeof(VBOXDISPIFESCAPE_SVGAGETSID))
        {
            Status = STATUS_INVALID_PARAMETER;
            break;
        }

        VBOXDISPIFESCAPE_SVGAGETSID *pGetSid = (VBOXDISPIFESCAPE_SVGAGETSID *)pEscapeHdr;

        DXGKARGCB_GETHANDLEDATA GetHandleData;
        GetHandleData.hObject = (D3DKMT_HANDLE)pGetSid->hAllocation;
        GetHandleData.Type = DXGK_HANDLE_ALLOCATION;
        GetHandleData.Flags.Value = 0;

        PVBOXWDDM_ALLOCATION pAllocation = (PVBOXWDDM_ALLOCATION)pDevExt->u.primary.DxgkInterface.DxgkCbGetHandleData(&GetHandleData);
        if (!pAllocation)
        {
            LOGE("failed to get allocation from handle");
            Status = STATUS_INVALID_PARAMETER;
            break;
        }

        if (pAllocation->enmType != VBOXWDDM_ALLOC_TYPE_D3D)
        {
            LOGE("Unexpected allocation type %d", pAllocation->enmType);
            Status = STATUS_INVALID_PARAMETER;
            break;
        }

        pGetSid->u32Sid = pAllocation->dx.sid;
        Status = STATUS_SUCCESS;
        break;
    }
    case VBOXESC_GALOGTOKMD:
    {
        if (pEscape->PrivateDriverDataSize < sizeof(VBOXDISPIFESCAPE_LOGTOKMD))
        {
            Status = STATUS_INVALID_PARAMETER;
            break;
        }
        VBOXDISPIFESCAPE_LOGTOKMD *pLogToKMD = (VBOXDISPIFESCAPE_LOGTOKMD *)pEscapeHdr;
        LOGE("[UMD]:%s\n", pLogToKMD->str);
        Status = STATUS_SUCCESS;
        break;
    }
    case VBOXESC_GETDISPSIZE:
    {
        if (pEscape->PrivateDriverDataSize < sizeof(VBOXDISPIFESCAPE_GETDISPSIZE))
        {
            Status = STATUS_INVALID_PARAMETER;
            break;
        }
        VBOXDISPIFESCAPE_GETDISPSIZE* pDispSize = (VBOXDISPIFESCAPE_GETDISPSIZE*)pEscapeHdr;
        VBOXWDDM_EXT_VMSVGA* pSvga = pDevExt->pGa->hw.pSvga;
        pDispSize->Width = SVGARegRead(pSvga, SVGA_REG_DISPLAY_WIDTH);
        pDispSize->Height = SVGARegRead(pSvga, SVGA_REG_DISPLAY_HEIGHT);
        RTRECTSIZE Resolution = { pDispSize->Width, pDispSize->Height };
        ExAcquireFastMutex(&pSvga->SvgaMutex);
        VBoxWddmVModesCleanup();
        for (UINT32 i = 0; i < (UINT32)VBoxCommonFromDeviceExt(pDevExt)->cDisplays; ++i)
            VBoxWddmVModesAdd(pDevExt, i, &Resolution, FALSE);
        ExReleaseFastMutex(&pSvga->SvgaMutex);

        Status = STATUS_SUCCESS;
        break;
    }
    case VBOXESC_CREATEDECODER: {
        if (pEscape->PrivateDriverDataSize < sizeof(VBOXDISPIFESCAPE_CREATEDECODER))
        {
            Status = STATUS_INVALID_PARAMETER;
            break;
        }
        VBOXDISPIFESCAPE_CREATEDECODER *createdecode = (VBOXDISPIFESCAPE_CREATEDECODER*)pEscapeHdr;
        VBOXWDDM_EXT_VMSVGA* pSvga = pDevExt->pGa->hw.pSvga;
        Status = SvgaCreateDecoder(pSvga, createdecode->u32DecoderId, &createdecode->desc,
                                   &createdecode->pic, pContext->pSvgaContext->u32Cid);
        break;
    }
    case VBOXESC_DESTROYDECODER: {
        if (pEscape->PrivateDriverDataSize < sizeof(VBOXDISPIFESCAPE_DECODERID)) {
            Status = STATUS_INVALID_PARAMETER;
            break;
        }
        VBOXDISPIFESCAPE_DECODERID *pDestroyDecoder = (VBOXDISPIFESCAPE_DECODERID*)pEscapeHdr;
        VBOXWDDM_EXT_VMSVGA* pSvga = pDevExt->pGa->hw.pSvga;
        KIRQL OldIrql;
        KeAcquireSpinLock(&pSvga->DecoderSpinLock, &OldIrql);
        GaIdFree(pSvga->au32DecoderBits, sizeof(pSvga->au32DecoderBits), VMSVGA_DXDECODER_ID_MAX,
                 pDestroyDecoder->u32DecoderId);
        KeReleaseSpinLock(&pSvga->DecoderSpinLock, OldIrql);
        Status = SvgaDestroyDecoder(pSvga, pDestroyDecoder->u32DecoderId);
        break;
    }
    case VBOXESC_GETDECODERID: {
        if (pEscape->PrivateDriverDataSize < sizeof(VBOXDISPIFESCAPE_DECODERID)) {
            Status = STATUS_INVALID_PARAMETER;
            break;
        }
        VBOXDISPIFESCAPE_DECODERID* pGetDecoderId = (VBOXDISPIFESCAPE_DECODERID*)pEscapeHdr;
        PVBOXWDDM_EXT_VMSVGA pSvga = pDevExt->pGa->hw.pSvga;
        uint32_t id;
        KIRQL OldIrql;
        KeAcquireSpinLock(&pSvga->DecoderSpinLock, &OldIrql);
        Status = GaIdAlloc(pSvga->au32DecoderBits, sizeof(pSvga->au32DecoderBits), VMSVGA_DXDECODER_ID_MAX, &id);
        KeReleaseSpinLock(&pSvga->DecoderSpinLock, OldIrql);
        if (NT_SUCCESS(Status)) {
            pGetDecoderId->u32DecoderId = id;
        }
        break;
    }
    case VBOXESC_SETVIDEODECODERT: {
        if (pEscape->PrivateDriverDataSize < sizeof(VBOXDISPIFESCAPE_SETVIDEODECODERT)) {
            Status = STATUS_INVALID_PARAMETER;
            break;
        }
        VBOXDISPIFESCAPE_SETVIDEODECODERT *pSetDecodeRT = (VBOXDISPIFESCAPE_SETVIDEODECODERT*)pEscapeHdr;
        VBOXWDDM_EXT_VMSVGA* pSvga = pDevExt->pGa->hw.pSvga;
        Status = SvgaSetDecoderRT(pSvga, pSetDecodeRT->u32DecoderId, pSetDecodeRT->u32Sid, pSetDecodeRT->u32SubIndex);
        break;
    }
    case VBOXESC_DECODEBEGINFRAME: {
        if (pEscape->PrivateDriverDataSize < sizeof(VBOXDISPIFESCAPE_DECODERID)) {
            Status = STATUS_INVALID_PARAMETER;
            break;
        }
        VBOXDISPIFESCAPE_DECODERID *pSetDecodeBeginFrame = (VBOXDISPIFESCAPE_DECODERID*)pEscapeHdr;
        VBOXWDDM_EXT_VMSVGA* pSvga = pDevExt->pGa->hw.pSvga;
        Status = SvgaSetDecoderBeginFrame(pSvga, pSetDecodeBeginFrame->u32DecoderId);
        break;
    }
    case VBOXESC_DECODEENDFRAME: {
        if (pEscape->PrivateDriverDataSize < sizeof(VBOXDISPIFESCAPE_DECODEENDFRAME)) {
            Status = STATUS_INVALID_PARAMETER;
            break;
        }
        VBOXDISPIFESCAPE_DECODEENDFRAME *pSetDecodeEndFrame = (VBOXDISPIFESCAPE_DECODEENDFRAME*)pEscapeHdr;
        VBOXWDDM_EXT_VMSVGA* pSvga = pDevExt->pGa->hw.pSvga;
        Status = SvgaSetDecoderEndFrame(pSvga, pSetDecodeEndFrame->u32DecoderId);
        break;
    }
    case VBOXESC_DECODEEXECUTE: {
        if (pEscape->PrivateDriverDataSize < sizeof(VBOXDISPIFESCAPE_DECODEEXECUTE)) {
            Status = STATUS_INVALID_PARAMETER;
            break;
        }
        VBOXDISPIFESCAPE_DECODEEXECUTE* pDecodeExecute = (VBOXDISPIFESCAPE_DECODEEXECUTE*)pEscapeHdr;
        VBOXWDDM_EXT_VMSVGA* pSvga = pDevExt->pGa->hw.pSvga;
        Status = SvgaSetDecoderExecute(pSvga, pDecodeExecute->ExeHeader.uVideoDecoderId,
                                       pDecodeExecute->ExeHeader.bufferCount,
                                       (VBOXDISPIFSCAPE_VIDEODECODERBUFFERDESC*)((uint8_t*)pEscapeHdr +
                                       sizeof(VBOXDISPIFESCAPE_DECODEEXECUTE)));
        break;
    }
    case VBOXESC_DECODESURFACEINFO: {
        if (pEscape->PrivateDriverDataSize < sizeof(VBOXDISPIFESCAPE_DECODESURFACEINFO)) {
            Status = STATUS_INVALID_PARAMETER;
            break;
        }
        VBOXDISPIFESCAPE_DECODESURFACEINFO* pDecodeSurfaceInfo = (VBOXDISPIFESCAPE_DECODESURFACEINFO*)pEscapeHdr;
        VBOXWDDM_EXT_VMSVGA* pSvga = pDevExt->pGa->hw.pSvga;
        Status = SvgaDecodeSurfaceInfo(pSvga, pDecodeSurfaceInfo->u32SurfaceCount, (uint32_t*)((uint8_t*)pEscapeHdr +
                                       sizeof(VBOXDISPIFESCAPE_DECODESURFACEINFO)));
        break;
    }
    case VBOXESC_STRETCHRECT: {
        if (pEscape->PrivateDriverDataSize < sizeof(VBOXDISPIFESCAPE_STRETCHRECT)) {
            Status = STATUS_INVALID_PARAMETER;
            break;
        }
        VBOXDISPIFESCAPE_STRETCHRECT* pStretchRect = (VBOXDISPIFESCAPE_STRETCHRECT*)pEscapeHdr;
        VBOXWDDM_EXT_VMSVGA* pSvga = pDevExt->pGa->hw.pSvga;
        Status = SvgaStretchRetc(pSvga, pStretchRect->uSrcSid, pStretchRect->uDstSid, pStretchRect->SrcRect,
                                 pStretchRect->DstRect, pContext->pSvgaContext->u32Cid);
        break;
     }
    default:
        break;
    }

    return Status;
}

NTSTATUS GaContextCreate(PVBOXWDDM_EXT_GA pGaDevExt,
                         PVBOXWDDM_CREATECONTEXT_INFO pInfo,
                         PVBOXWDDM_CONTEXT pContext)
{
    VBOXWDDM_EXT_VMSVGA *pSvga = pGaDevExt->hw.pSvga;
    UINT32 u32Cid;
    NTSTATUS Status;

    /*
     * Allocate SVGA context and initialize it.
     */
    PVMSVGACONTEXT pSvgaContext = (PVMSVGACONTEXT)GaMemAllocZero(sizeof(VMSVGACONTEXT));
    AssertReturn(pSvgaContext, STATUS_INSUFFICIENT_RESOURCES);

    pSvgaContext->fDXContext = RT_BOOL(pInfo->u.vmsvga.u32Flags & VBOXWDDM_F_GA_CONTEXT_VGPU10);
    LOGI("DXContext:%d\n", pSvgaContext->fDXContext);
    if (pSvgaContext->fDXContext)
        Status = SvgaDXContextIdAlloc(pSvga, &u32Cid);
    else
        Status = SvgaContextIdAlloc(pSvga, &u32Cid);
    if (NT_SUCCESS(Status))
    {
        if (pSvgaContext->fDXContext)
            Status = SvgaDXContextCreate(pSvga, u32Cid, pInfo->appName, pInfo->appNameLen);
        else
            Status = SvgaContextCreate(pSvga, u32Cid);
        if (Status == STATUS_SUCCESS)
        {
            /* Init pContext fields, which are relevant to the gallium context. */
            pSvgaContext->u32Cid = u32Cid;
            LOGI("pGaDevExt = %p, cid = %d\n", pGaDevExt, u32Cid);
        }
        else
        {
            if (pSvgaContext->fDXContext)
                SvgaDXContextIdFree(pSvga, u32Cid);
            else
                SvgaContextIdFree(pSvga, u32Cid);
            LOGE("Create context failed!\n");
        }
    }
    else
    {
        LOGE("Alloc contextID failed!\n");
    }

    if (NT_SUCCESS(Status))
        pContext->pSvgaContext = pSvgaContext;
    else
        GaMemFree(pSvgaContext);

    return Status;
}

NTSTATUS GaContextDestroy(PVBOXWDDM_EXT_GA pGaDevExt,
                          PVBOXWDDM_CONTEXT pContext)
{
    VBOXWDDM_EXT_VMSVGA *pSvga = pGaDevExt->hw.pSvga;
    PVMSVGACONTEXT pSvgaContext = pContext->pSvgaContext;
    if (!pSvgaContext)
        return STATUS_SUCCESS;

    pContext->pSvgaContext = NULL;

    LOGI("u32Cid = %d\n", pSvgaContext->u32Cid);

    NTSTATUS Status;
    if (pSvgaContext->fDXContext)
    {
        for (unsigned i = 0; i < RT_ELEMENTS(pSvgaContext->aCOT); ++i)
        {
            PVMSVGACOT pCOT = &pSvgaContext->aCOT[i];
            if (pCOT->pMob)
            {
                void *pvCmd = SvgaCmdBuf3dCmdReserve(pSvga, SVGA_3D_CMD_DX_SET_COTABLE, sizeof(SVGA3dCmdDXSetCOTable), SVGA3D_INVALID_ID);
                if (pvCmd)
                {
                    SVGA3dCmdDXSetCOTable *pCmd = (SVGA3dCmdDXSetCOTable *)pvCmd;
                    pCmd->cid              = pSvgaContext->u32Cid;
                    pCmd->mobid            = SVGA3D_INVALID_ID;
                    pCmd->type             = (SVGACOTableType)i;
                    pCmd->validSizeInBytes = 0;
                    SvgaCmdBufCommit(pSvga, sizeof(*pCmd));
                }

                UINT32 cbRequired = 0;

                SvgaMobDestroy(pSvga, pCOT->pMob, NULL, 0, &cbRequired);
                pvCmd = SvgaCmdBufReserve(pSvga, cbRequired, SVGA3D_INVALID_ID);
                if (pvCmd)
                {
                    SvgaMobDestroy(pSvga, pCOT->pMob, pvCmd, cbRequired, &cbRequired);
                    SvgaCmdBufCommit(pSvga, cbRequired);
                }

                pCOT->pMob = NULL;
            }
        }

        SvgaDXContextDestroy(pSvga, pSvgaContext->u32Cid);
        Status = SvgaDXContextIdFree(pSvga, pSvgaContext->u32Cid);
    }
    else
    {
        SvgaContextDestroy(pSvga, pSvgaContext->u32Cid);
        Status = SvgaContextIdFree(pSvga, pSvgaContext->u32Cid);
    }

    SvgaFlush(pSvga);
    GaMemFree(pSvgaContext);
    return Status;
}

DECLINLINE(int) gaFenceCmp(UINT32 u32FenceA, UINT32 u32FenceB)
{
     if (u32FenceA < u32FenceB || u32FenceA - u32FenceB > MAX_UINT32 / 2)
     {
         return -1; /* FenceB is newer than FenceA. */
     }
     else if (u32FenceA == u32FenceB)
     {
         /* FenceA is equal to FenceB. */
         return 0;
     }

     /* FenceB is older than FenceA. */
     return 1;
}

DECLINLINE(int) gaFenceCmp64(UINT64 u32FenceA, UINT64 u32FenceB)
{
     if (u32FenceA < u32FenceB || u32FenceA - u32FenceB > MAX_UINT64 / 2)
     {
         return -1; /* FenceB is newer than FenceA. */
     }
     else if (u32FenceA == u32FenceB)
     {
         /* FenceA is equal to FenceB. */
         return 0;
     }

     /* FenceB is older than FenceA. */
     return 1;
}

static void dxgkNotifyDma(DXGKRNL_INTERFACE *pDxgkInterface,
                          DXGK_INTERRUPT_TYPE enmType,
                          UINT uNodeOrdinal,
                          UINT uFenceId,
                          UINT uLastCompletedFenceId)
{
    DXGKARGCB_NOTIFY_INTERRUPT_DATA notify;
    memset(&notify, 0, sizeof(notify));

    LOGI("%d fence %d\n", enmType, uFenceId);
    switch (enmType)
    {
        case DXGK_INTERRUPT_DMA_COMPLETED:
            notify.InterruptType = DXGK_INTERRUPT_DMA_COMPLETED;
            notify.DmaCompleted.SubmissionFenceId = uFenceId;
            notify.DmaCompleted.NodeOrdinal = uNodeOrdinal;
            break;

        case DXGK_INTERRUPT_DMA_PREEMPTED:
            notify.InterruptType = DXGK_INTERRUPT_DMA_PREEMPTED;
            notify.DmaPreempted.PreemptionFenceId = uFenceId;
            notify.DmaPreempted.NodeOrdinal = uNodeOrdinal;
            notify.DmaPreempted.LastCompletedFenceId = uLastCompletedFenceId;
            break;

        case DXGK_INTERRUPT_DMA_FAULTED:
            notify.InterruptType = DXGK_INTERRUPT_DMA_FAULTED;
            notify.DmaFaulted.FaultedFenceId = uFenceId;
            notify.DmaFaulted.Status = STATUS_UNSUCCESSFUL;
            notify.DmaFaulted.NodeOrdinal = uNodeOrdinal;
            break;

        default:
            LOGW("completion type %d", enmType);
            break;
    }

    if (notify.InterruptType)
    {
        pDxgkInterface->DxgkCbNotifyInterrupt(pDxgkInterface->DeviceHandle, &notify);
        LOGI("notified\n");
    }
}

static void gaReportFence(PVBOXMP_DEVEXT pDevExt)
{
    VBOXWDDM_EXT_GA *pGaDevExt = pDevExt->pGa;
    PVBOXWDDM_EXT_VMSVGA pSvga = pGaDevExt->hw.pSvga;
    /* Read the last completed fence from the device. */
    const UINT32 u32Fence = SVGAFifoRead(pSvga, SVGA_FIFO_FENCE);
    UINT32 nextcmd = SVGAFifoRead(pSvga, SVGA_FIFO_NEXT_CMD);
    LOGI("GA FIFO NEXT CMD=%d\n", nextcmd);

    LOGI("Fence %u\n", u32Fence);

    if (u32Fence == ASMAtomicReadU32(&pGaDevExt->u32PreemptionFenceId))
    {
        ASMAtomicWriteU32(&pGaDevExt->u32PreemptionFenceId, 0);

        const UINT32 u32LastSubmittedFenceId = ASMAtomicReadU32(&pGaDevExt->u32LastSubmittedFenceId);
        ASMAtomicWriteU32(&pGaDevExt->u32LastCompletedFenceId, u32LastSubmittedFenceId);

        dxgkNotifyDma(&pDevExt->u.primary.DxgkInterface, DXGK_INTERRUPT_DMA_PREEMPTED,
                      0, u32Fence, u32LastSubmittedFenceId);

        /* Notify DXGK about the updated DMA fence. */
        pDevExt->u.primary.DxgkInterface.DxgkCbQueueDpc(pDevExt->u.primary.DxgkInterface.DeviceHandle);
    }
    else
    {
        /* Check if we already reported it. */
        const UINT32 u32LastCompletedFenceId = ASMAtomicReadU32(&pGaDevExt->u32LastCompletedFenceId);
        // For fast startup, the u32Fence is increasing after each startup (not reset to zero),
        // the u32fence might overflow the MAX_U32/2, which will make gaFenceCmp check failed,
        // thus for the first report fence op of each startup, just update fence without gaFenceCmp
        if (gaFenceCmp(u32LastCompletedFenceId, u32Fence) < 0)
        {
            /* u32Fence is newer. */
            ASMAtomicWriteU32(&pGaDevExt->u32LastCompletedFenceId, u32Fence);

            dxgkNotifyDma(&pDevExt->u.primary.DxgkInterface, DXGK_INTERRUPT_DMA_COMPLETED,
                          0, u32Fence, u32Fence);

            /* Notify DXGK about the updated DMA fence. */
            pDevExt->u.primary.DxgkInterface.DxgkCbQueueDpc(pDevExt->u.primary.DxgkInterface.DeviceHandle);
        }
    }
}

BOOLEAN GaDxgkDdiInterruptRoutine(const PVOID MiniportDeviceContext,
                                  ULONG MessageNumber)
{
    RT_NOREF(MessageNumber);

    LOGI("==> MiniportDeviceContext(0x%p)\n", MiniportDeviceContext);
    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)MiniportDeviceContext;

    VBOXWDDM_EXT_GA *pGaDevExt = pDevExt->pGa;
    if (!pGaDevExt)
    {
        /* Device is not initialized yet. Not a Gallium interrupt, "return FALSE immediately". */
        LOGE("!pGaDevExt return\n");
        return FALSE;
    }

    PVBOXWDDM_EXT_VMSVGA pSvga = pGaDevExt->hw.pSvga;
    if (!pSvga)
    {
        /* Device is not initialized yet. Not a VMSVGA interrupt, "return FALSE immediately". */
        LOGE("!pSvga return\n");
        return FALSE;
    }

    const uint32_t u32IrqStatus = SVGAPortRead(pSvga, SVGA_IRQSTATUS_PORT);
    if (!u32IrqStatus)
    {
        /* Not a VMSVGA interrupt, "return FALSE immediately". */
        LOGE("!u32IrqStatus return\n");
        return FALSE;
    }

    SVGAPortWrite(pSvga, SVGA_IRQSTATUS_PORT, u32IrqStatus);

    KeMemoryBarrier();

    /* Check what happened. */
    if (u32IrqStatus & SVGA_IRQFLAG_ANY_FENCE)
    {
        /* A SVGA_CMD_FENCE command has been processed by the device. */
        LOGI("GaDxgkDdiInterruptRoutine call gaReportFence!\n");
        gaReportFence(pDevExt);
    }

    if (u32IrqStatus & SVGA_IRQFLAG_DISPLAY_CHANGE) {
        ASMAtomicWriteBool(&pSvga->fConfigIrq, true);
    }

    if (u32IrqStatus & (SVGA_IRQFLAG_COMMAND_BUFFER | SVGA_IRQFLAG_ERROR))
        ASMAtomicWriteBool(&pSvga->fCommandBufferIrq, true);

    pDevExt->u.primary.DxgkInterface.DxgkCbQueueDpc(pDevExt->u.primary.DxgkInterface.DeviceHandle);

    LOGI("<== MiniportDeviceContext(0x%p)\n", MiniportDeviceContext);

    return TRUE;
}

static void dxChangeWindowSizeNotify(PVOID IoObject, PVOID Context, PIO_WORKITEM IoWorkItem)
{
    RT_NOREF(IoObject);
    IoFreeWorkItem(IoWorkItem);

    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)Context;
    PVBOXWDDM_EXT_VMSVGA pSvga = pDevExt->pGa->hw.pSvga;
    if (!pSvga) {
        LOGE("pSvga is not initialized\n");
        return;
    }

    VBoxWddmVModesCleanup();
    UpdateChildStatus(pDevExt, FALSE);
    VBoxWddmVModesInit(pDevExt, TRUE);
    // Reconnect screen
    UpdateChildStatus(pDevExt, TRUE);

    ASMAtomicDecU32(&pSvga->asyncWorkCount);
}

static void dxDeferredMobDestruction(PVOID IoObject, PVOID Context, PIO_WORKITEM IoWorkItem)
{
    RT_NOREF(IoObject);
    IoFreeWorkItem(IoWorkItem);

    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)Context;

    if (!pDevExt || !pDevExt->pGa || !pDevExt->pGa->hw.pSvga) {
        LOGE("Device is not initialized\n");
        return;
    }

    PVBOXWDDM_EXT_VMSVGA pSvga = pDevExt->pGa->hw.pSvga;

    if (pSvga->pMiniportMobData)
    {
        UINT64 const u64MobFence = ASMAtomicReadU64(&pSvga->pMiniportMobData->u64MobFence);

        /* Move mobs which were deleted by the host to the local list under the lock. */
        RTLISTANCHOR listDestroyedMobs;
        RTListInit(&listDestroyedMobs);

        KIRQL OldIrql;
        SvgaHostObjectsLock(pSvga, &OldIrql);

        PVMSVGAMOB pIter, pNext;
        RTListForEachSafe(&pSvga->listMobDeferredDestruction, pIter, pNext, VMSVGAMOB, node)
        {
            if (gaFenceCmp64(pIter->u64MobFence, u64MobFence) <= 0)
            {
                RTListNodeRemove(&pIter->node);
                RTListAppend(&listDestroyedMobs, &pIter->node);
            }
        }

        SvgaHostObjectsUnlock(pSvga, OldIrql);

        RTListForEachSafe(&listDestroyedMobs, pIter, pNext, VMSVGAMOB, node)
        {
            /* Delete the data. SvgaMobFree deallocates pIter. */
            RTListNodeRemove(&pIter->node);
            SvgaMobFree(pSvga, pIter);
        }
    }

    ULONG64 QpcTsIgnored;
    UINT64 dpcTime;

    PVBOXWDDM_POINTER_INFO pPointerInfo = &pDevExt->aSources[0].PointerInfo;
    if (!pPointerInfo) {
        LOGE("PointerInfo is not initilized\n");
        ASMAtomicDecU32(&pSvga->asyncWorkCount);
        return;
    }

    // Wait 200ms after the last no visile cursor cmd, make host cursor transparent
    if (ASMAtomicReadU32(&pPointerInfo->hasPendingNoVisible) != 0) {
        dpcTime = KeQueryInterruptTimePrecise(&QpcTsIgnored);
        if (dpcTime - pPointerInfo->lastNoVisibleTime > 2000000 && ASMAtomicCmpXchgU32(&pPointerInfo->hasPendingNoVisible, 0, 1) == 1) {
            pPointerInfo->Attributes.data.Enable &= ~VBOX_MOUSE_POINTER_VISIBLE;
            // Cancel pending set shape of hw cursor because it will change into soft cursor
            ASMAtomicWriteU32(&pPointerInfo->hasPendingSetShape, 0);
            vboxWddmSendTransparentHostCursor(pDevExt);
        }
    }

    if (ASMAtomicReadU32(&pPointerInfo->hasPendingSetShape) != 0) {
        dpcTime = KeQueryInterruptTimePrecise(&QpcTsIgnored);
        if (dpcTime - pPointerInfo->lastSetShapeTime > 2000000 && ASMAtomicCmpXchgU32(&pPointerInfo->hasPendingSetShape, 0, 1) == 1) {
            vboxWddmUpdatePointerShape(pDevExt, &pPointerInfo->Attributes.data, VBOXWDDM_POINTER_ATTRIBUTES_SIZE);
        }
    }

    ASMAtomicDecU32(&pSvga->asyncWorkCount);
}

VOID GaDxgkDdiDpcRoutine(const PVOID MiniportDeviceContext)
{
    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)MiniportDeviceContext;
    if (!pDevExt) {
        LOGE("DevExt is not initialized\n");
        return;
    }

    VBOXWDDM_EXT_GA *pGaDevExt = pDevExt->pGa;
    if (!pGaDevExt)
    {
        /* Device is not initialized yet. */
        LOGE("GaDevExt is not initialized\n");
        return;
    }

    PVBOXWDDM_EXT_VMSVGA pSvga = pGaDevExt->hw.pSvga;
    if (!pSvga)
    {
        /* Device is not initialized yet. */
        LOGE("Svga is not initialized\n");
        return;
    }

    ASMAtomicIncU32(&pSvga->asyncWorkCount);

    /* Scan fence objects and mark all with u32FenceId < u32LastCompletedFenceId as SIGNALED */
    const UINT32 u32LastCompletedFenceId = ASMAtomicReadU32(&pGaDevExt->u32LastCompletedFenceId);

    gaFenceObjectsLock(pGaDevExt);

    GAFENCEOBJECT *pIter, *pNext;
    RTListForEachSafe(&pGaDevExt->fenceObjects.list, pIter, pNext, GAFENCEOBJECT, node)
    {
        if (pIter->u32FenceState == GAFENCE_STATE_SUBMITTED)
        {
            if (gaFenceCmp(pIter->u32SubmissionFenceId, u32LastCompletedFenceId) <= 0)
            {

                ASMAtomicWriteU32(&pGaDevExt->u32LastCompletedSeqNo, pIter->u32SeqNo);
                pIter->u32FenceState = GAFENCE_STATE_SIGNALED;
                if (RT_BOOL(pIter->fu32FenceFlags & GAFENCE_F_WAITED))
                {
                    KeSetEvent(&pIter->event, 0, FALSE);
                }

                GaFenceUnrefLocked(pGaDevExt, pIter);
            }
        }
    }

    gaFenceObjectsUnlock(pGaDevExt);

    KIRQL OldIrql;
    SvgaHostObjectsLock(pSvga, &OldIrql);

    /* Move the completed render data objects to the local list under the lock. */
    RTLISTANCHOR listHwRenderData;
    RTListInit(&listHwRenderData);

    if (!RTListIsEmpty(&pGaDevExt->listHwRenderData))
    {
        GAHWRENDERDATA *pIter1, *pNext1;
        RTListForEachSafe(&pGaDevExt->listHwRenderData, pIter1, pNext1, GAHWRENDERDATA, node)
        {
            if (gaFenceCmp(pIter1->u32SubmissionFenceId, u32LastCompletedFenceId) <= 0)
            {
                RTListNodeRemove(&pIter1->node);
                RTListAppend(&listHwRenderData, &pIter1->node);
            }
        }
    }

    SvgaHostObjectsUnlock(pSvga, OldIrql);

    if (!RTListIsEmpty(&listHwRenderData))
    {
        GAHWRENDERDATA *pIter2, *pNext2;
        RTListForEachSafe(&listHwRenderData, pIter2, pNext2, GAHWRENDERDATA, node)
        {
            /* Delete the data. SvgaRenderComplete deallocates pIter. */
            RTListNodeRemove(&pIter2->node);
            SvgaRenderComplete(pSvga, pIter2);
        }
    }

    if (ASMAtomicCmpXchgBool(&pSvga->fCommandBufferIrq, false, true) && pSvga->pCBState)
        SvgaCmdBufProcess(pSvga);

    if (ASMAtomicCmpXchgBool(&pSvga->fConfigIrq, false, true))
    {
        PIO_WORKITEM pWorkItem = IoAllocateWorkItem(pDevExt->pPDO);
        if (pWorkItem) {
            ASMAtomicIncU32(&pSvga->asyncWorkCount);
            IoQueueWorkItemEx(pWorkItem, dxChangeWindowSizeNotify, DelayedWorkQueue, pDevExt);
        }
    }

    /*
    * Deferred MOB destruction.
    */
    SvgaHostObjectsLock(pSvga, &OldIrql);
    bool fMobs = !RTListIsEmpty(&pSvga->listMobDeferredDestruction);
    SvgaHostObjectsUnlock(pSvga, OldIrql);

    PVBOXWDDM_POINTER_INFO pPointerInfo = &pDevExt->aSources[0].PointerInfo;
    if (fMobs || ASMAtomicReadU32(&pPointerInfo->hasPendingNoVisible) != 0 ||
        ASMAtomicReadU32(&pPointerInfo->hasPendingSetShape) != 0)
    {
        /* Deallocate memory in a worker thread at PASSIVE_LEVEL. */
        PIO_WORKITEM pWorkItem = IoAllocateWorkItem(pDevExt->pPDO);
        if (pWorkItem) {
            ASMAtomicIncU32(&pSvga->asyncWorkCount);
            IoQueueWorkItemEx(pWorkItem, dxDeferredMobDestruction, DelayedWorkQueue, pDevExt);
        }
    }

    ASMAtomicDecU32(&pSvga->asyncWorkCount);
}

NTSTATUS GaQueryInfo(PVBOXWDDM_EXT_GA pGaDevExt,
                     VBOXVIDEO_HWTYPE enmHwType,
                     VBOXGAHWINFO *pHWInfo)
{
    NTSTATUS Status = STATUS_SUCCESS;

    if (!pGaDevExt) {
        LOGE("Svga driver not ready\n");
        return STATUS_INVALID_PARAMETER;
    }

    switch (enmHwType)
    {
        case VBOXVIDEO_HWTYPE_VMSVGA:
            pHWInfo->u32HwType = VBOX_GA_HW_TYPE_VMSVGA;
            break;
        default:
            LOGE("HW type not support.\n");
            Status = STATUS_NOT_SUPPORTED;
    }

    if (NT_SUCCESS(Status))
    {
        pHWInfo->u32Reserved = 0;
        memset(pHWInfo->u.au8Raw, 0, sizeof(pHWInfo->u.au8Raw));

        if (pHWInfo->u32HwType == VBOX_GA_HW_TYPE_VMSVGA)
        {
            Status = SvgaQueryInfo(pGaDevExt->hw.pSvga, &pHWInfo->u.svga);
        }
        else
        {
            LOGE("HW type not support.\n");
            Status = STATUS_NOT_SUPPORTED;
        }
    }

    return Status;
}

NTSTATUS APIENTRY GaDxgkDdiSubmitCommand(const HANDLE hAdapter, const DXGKARG_SUBMITCOMMAND *pSubmitCommand)
{
    LOGI("==> hAdapter(0x%p)\n" , hAdapter);
    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)hAdapter;
    PVBOXWDDM_CONTEXT pContext = (PVBOXWDDM_CONTEXT)pSubmitCommand->hContext;
    VBOXWDDM_EXT_GA *pGaDevExt = pDevExt->pGa;
    BOOL bFenceOnly = FALSE;

    LOGI("pContext %p, fence %d\n", pContext, pSubmitCommand->SubmissionFenceId);

    const UINT32 cbPrivateData = pSubmitCommand->DmaBufferPrivateDataSubmissionEndOffset - pSubmitCommand->DmaBufferPrivateDataSubmissionStartOffset;
    void *pvPrivateData = (UINT8 *)pSubmitCommand->pDmaBufferPrivateData + pSubmitCommand->DmaBufferPrivateDataSubmissionStartOffset;

    LOGI("DmaBuffer (fence %d): End %d, Start %d\n",
         pSubmitCommand->SubmissionFenceId, pSubmitCommand->DmaBufferSubmissionEndOffset,
         pSubmitCommand->DmaBufferSubmissionStartOffset);
    LOGI("PrivateData (fence %d): End %d, Start %d, cb %d\n",
         pSubmitCommand->SubmissionFenceId, pSubmitCommand->DmaBufferPrivateDataSubmissionEndOffset,
         pSubmitCommand->DmaBufferPrivateDataSubmissionStartOffset, cbPrivateData);

    UINT32 cbDmaBufferSubmission = pSubmitCommand->DmaBufferSubmissionEndOffset - pSubmitCommand->DmaBufferSubmissionStartOffset;
    UINT32 cDataBlocks = cbPrivateData / sizeof(GARENDERDATA);

    if (cDataBlocks == 0)
    {
        /* Sometimes a zero sized paging buffer is submitted.
         * Seen this on W10.17763 right after DXGK_OPERATION_DISCARD_CONTENT.
         * Can not ignore such block, since a new SubmissionFenceId is passed.
         * Try to handle it by emitting the fence command only.
         */
        LOGW("WDDM: empty buffer: cbPrivateData %d, flags 0x%x\n", cbPrivateData, pSubmitCommand->Flags.Value);
    }

    GARENDERDATA const *pRenderData = (GARENDERDATA *)pvPrivateData;

    if (cDataBlocks == 1 && pRenderData->u32DataType == GARENDERDATA_TYPE_FENCE)
        bFenceOnly = TRUE;

    uint32_t cbData = 0;
    while (cDataBlocks--)
    {
        LOGI("pRenderData %p: u32DataType %u, pvDmaBuffer %p, cbData %u\n",
                pRenderData, pRenderData->u32DataType, pRenderData->pvDmaBuffer, pRenderData->cbData);

        cbData += pRenderData->cbData;
        AssertReturn(cbDmaBufferSubmission >= pRenderData->cbData, STATUS_INVALID_PARAMETER);

        if (pRenderData->pFenceObject)
        {
            GAFENCEOBJECT * const pFO = pRenderData->pFenceObject;
            LOGI("pFO = %p, u32FenceHandle = %d, Fence = %d\n",
                   pFO, pFO->u32FenceHandle, pSubmitCommand->SubmissionFenceId);

            gaFenceObjectsLock(pGaDevExt);

            Assert(pFO->u32FenceState == GAFENCE_STATE_IDLE);
            pFO->u32SubmissionFenceId = pSubmitCommand->SubmissionFenceId;
            pFO->u32FenceState = GAFENCE_STATE_SUBMITTED;
            pFO->u64SubmittedTS = KeQueryInterruptTime() * 100;

            gaFenceObjectsUnlock(pGaDevExt);
        }

        if (pRenderData->pHwRenderData)
        {
            GAHWRENDERDATA * const pHwRenderData = pRenderData->pHwRenderData;
            pHwRenderData->u32SubmissionFenceId = pSubmitCommand->SubmissionFenceId;
            pHwRenderData->u32Reserved = 0;

            KIRQL OldIrql;
            SvgaHostObjectsLock(pGaDevExt->hw.pSvga, &OldIrql);
            RTListAppend(&pGaDevExt->listHwRenderData, &pHwRenderData->node);
            SvgaHostObjectsUnlock(pGaDevExt->hw.pSvga, OldIrql);
        }

        ++pRenderData;
    }

    if (cbDmaBufferSubmission && !bFenceOnly)
    {
        if (pGaDevExt->hw.pSvga->pCBState)
        {
            PVMSVGACONTEXT pSvgaContext = pContext->pSvgaContext;
            uint32_t const cid = (pSvgaContext && pSvgaContext->fDXContext) ? pSvgaContext->u32Cid : SVGA3D_INVALID_ID;

            PHYSICAL_ADDRESS phys = pSubmitCommand->DmaBufferPhysicalAddress;
            phys.QuadPart += pSubmitCommand->DmaBufferSubmissionStartOffset;

            PVMSVGACB pCB;
            NTSTATUS Status = SvgaCmdBufAllocUMD(pGaDevExt->hw.pSvga, phys,
                                                 pSubmitCommand->DmaBufferSize - pSubmitCommand->DmaBufferSubmissionStartOffset,
                                                 cbDmaBufferSubmission, cid, &pCB);
            LOGI("Allocated UMD buffer %p\n", pCB);
            if (NT_SUCCESS(Status))
            {
                Status = SvgaCmdBufSubmitUMD(pGaDevExt->hw.pSvga, pCB);
                if (!NT_SUCCESS(Status)) {
                    LOGE("submit umd cb failed\n");
                }
                Assert(NT_SUCCESS(Status)); RT_NOREF(Status);
            }
        }
        else
        {
            Assert(pSubmitCommand->DmaBufferSegmentId == 0);

            /* This requires the virtual address of the buffer, which is stored in RenderData. */
            if (cbPrivateData >= sizeof(GARENDERDATA))
            {
                pRenderData = (GARENDERDATA *)pvPrivateData;
                if (pRenderData->pvDmaBuffer)
                {
                    void *pvDmaBuffer = (uint8_t *)pRenderData->pvDmaBuffer + pSubmitCommand->DmaBufferSubmissionStartOffset;
                    uint32_t const cbSubmit = cbDmaBufferSubmission;

                    /* Copy DmaBuffer to Fifo. */
                    void *pvCmd = SvgaFifoReserve(pGaDevExt->hw.pSvga, cbSubmit);
                    AssertPtrReturn(pvCmd, STATUS_INSUFFICIENT_RESOURCES);

                    /* pvDmaBuffer is the actual address of the current data block.
                     * Therefore do not use pSubmitCommand->DmaBufferSubmissionStartOffset here.
                     */
                    memcpy(pvCmd, pvDmaBuffer, cbSubmit);
                    SvgaFifoCommit(pGaDevExt->hw.pSvga, cbSubmit);
                }
            }
        }
    }

    ASMAtomicWriteU32(&pGaDevExt->u32LastSubmittedFenceId, pSubmitCommand->SubmissionFenceId);

    /* Submit the fence. */
    if (pGaDevExt->hw.pSvga->pCBState)
    {
        struct
        {
            uint32_t id;
            uint32_t fence;
        } fence;
        fence.id = SVGA_CMD_FENCE;
        fence.fence = pSubmitCommand->SubmissionFenceId;
        NTSTATUS Status = STATUS_SUCCESS;
        Status = SvgaCmdBufSubmitMiniportCommand(pGaDevExt->hw.pSvga, &fence, sizeof(fence));
        if (!NT_SUCCESS(Status)) {
            // Submit fence cmd failed, just skip it and report completed to dxgk
            LOGE("submit mini port cmd failed, skip the fence\n");
            UINT32 u32Fence = pSubmitCommand->SubmissionFenceId;
            dxgkNotifyDma(&pDevExt->u.primary.DxgkInterface, DXGK_INTERRUPT_DMA_COMPLETED, 0, u32Fence, u32Fence);
            pDevExt->u.primary.DxgkInterface.DxgkCbQueueDpc(pDevExt->u.primary.DxgkInterface.DeviceHandle);
        }
    }
    else
        SvgaFence(pGaDevExt->hw.pSvga, pSubmitCommand->SubmissionFenceId);

    LOGI("done %d\n", pSubmitCommand->SubmissionFenceId);
    return STATUS_SUCCESS;
}

typedef struct GAPREEMPTCOMMANDCBCTX
{
    PVBOXMP_DEVEXT pDevExt;
    UINT uPreemptionFenceId;
    UINT uLastCompletedFenceId;
} GAPREEMPTCOMMANDCBCTX;

static BOOLEAN gaPreemptCommandCb(PVOID Context)
{
    GAPREEMPTCOMMANDCBCTX *pCtx = (GAPREEMPTCOMMANDCBCTX *)Context;
    dxgkNotifyDma(&pCtx->pDevExt->u.primary.DxgkInterface, DXGK_INTERRUPT_DMA_PREEMPTED,
                  0, pCtx->uPreemptionFenceId, pCtx->uLastCompletedFenceId);
    return TRUE;
}

NTSTATUS APIENTRY GaDxgkDdiPreemptCommand(const HANDLE hAdapter,
                                          const DXGKARG_PREEMPTCOMMAND *pPreemptCommand)
{
    NTSTATUS Status;

    LOGI("hAdapter %p, fence %d\n", hAdapter, pPreemptCommand->PreemptionFenceId);

    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)hAdapter;
    VBOXWDDM_EXT_GA *pGaDevExt = pDevExt->pGa;
    if (!pGaDevExt)
    {
        /* Device is not initialized yet. */
        return STATUS_SUCCESS;
    }

    /* We can not safely remove submitted data from FIFO, so just let the host process all submitted commands.
     */
    const UINT32 u32LastCompletedFenceId = ASMAtomicReadU32(&pGaDevExt->u32LastCompletedFenceId);
    const UINT32 u32LastSubmittedFenceId = ASMAtomicReadU32(&pGaDevExt->u32LastSubmittedFenceId);
    if (u32LastCompletedFenceId == u32LastSubmittedFenceId)
    {
        GAPREEMPTCOMMANDCBCTX Ctx;
        Ctx.pDevExt = pDevExt;
        Ctx.uPreemptionFenceId = pPreemptCommand->PreemptionFenceId;
        Ctx.uLastCompletedFenceId = u32LastCompletedFenceId;

        DXGKRNL_INTERFACE *pDxgkInterface = &pDevExt->u.primary.DxgkInterface;
        BOOLEAN bReturnValue = FALSE;
        Status = pDxgkInterface->DxgkCbSynchronizeExecution(pDxgkInterface->DeviceHandle,
                                                            gaPreemptCommandCb, &Ctx, 0, &bReturnValue);
    }
    else
    {
        if (pGaDevExt->hw.pSvga->pCBState)
        {
#if 0 /** @todo later */
            struct
            {
                uint32_t id;
                SVGADCCmdPreempt cmd;
            } cmd;
            cmd.id = SVGA_DC_CMD_PREEMPT;
            cmd.cmd.context = SVGA_CB_CONTEXT_0;
            cmd.cmd.ignoreIDZero = 1; /* Preempt only the UMD buffers. */
            Status = SvgaCmdBufDeviceCommand(pGaDevExt->hw.pSvga, &cmd, sizeof(cmd));
            AssertReturn(NT_SUCCESS(Status), Status);
#endif

            Assert(pGaDevExt->u32PreemptionFenceId == 0);
            ASMAtomicWriteU32(&pGaDevExt->u32PreemptionFenceId, pPreemptCommand->PreemptionFenceId);

            struct
            {
                uint32_t id;
                uint32_t fence;
            } fence;
            fence.id = SVGA_CMD_FENCE;
            fence.fence = pPreemptCommand->PreemptionFenceId;
            Status = SvgaCmdBufSubmitMiniportCommand(pGaDevExt->hw.pSvga, &fence, sizeof(fence));
        }
        else
        {
            /* We can not safely remove submitted data from FIFO, so just let the host process all submitted commands.
             */
            /* Submit the fence. */
            Assert(pGaDevExt->u32PreemptionFenceId == 0);
            ASMAtomicWriteU32(&pGaDevExt->u32PreemptionFenceId, pPreemptCommand->PreemptionFenceId);
            Status = SvgaFence(pGaDevExt->hw.pSvga, pPreemptCommand->PreemptionFenceId);
        }
    }

    return Status;
}

static BOOLEAN gaQueryCurrentFenceCb(PVOID Context)
{
    LOGE("gaQueryCurrentFenceCb call gaReportFence!!!!!!!!!!\n");
    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)Context;
    VBOXWDDM_EXT_GA *pGaDevExt = pDevExt->pGa;
    if (!pGaDevExt) {
        LOGE("!pGaDevExt return\n");
        return TRUE;
    }
    PVBOXWDDM_EXT_VMSVGA pSvga = pGaDevExt->hw.pSvga;
    if (!pSvga) {
        LOGE("!pSvga return\n");
        return TRUE;
    }

    /* Read the last completed fence from the device. */
    const UINT32 u32Fence = SVGAFifoRead(pSvga, SVGA_FIFO_FENCE);
    LOGE("QueryFenceCb gaReportFence--u32LastSubmittedFenceId=%u, u32LastCompletedFenceId=%u, u32FenceId=%u\n",
         pGaDevExt->u32LastSubmittedFenceId, pGaDevExt->u32LastCompletedFenceId, u32Fence);

    gaReportFence(pDevExt);
    return TRUE;
}

NTSTATUS APIENTRY GaDxgkDdiQueryCurrentFence(const HANDLE hAdapter,
                                             DXGKARG_QUERYCURRENTFENCE *pCurrentFence)
{
    LOGI("==>, hAdapter(0x%p)\n", hAdapter);
    LOGI("pCurrentFence->NodeOrdinal=%u pCurrentFence->EngineOrdinal=%u \n",
         pCurrentFence->NodeOrdinal, pCurrentFence->EngineOrdinal);
    NTSTATUS Status;

    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)hAdapter;
    VBOXWDDM_EXT_GA *pGaDevExt = pDevExt->pGa;
    if (!pGaDevExt)
    {
        /* Device is not initialized yet. */
        return STATUS_SUCCESS;
    }

    DXGKRNL_INTERFACE *pDxgkInterface = &pDevExt->u.primary.DxgkInterface;
    LARGE_INTEGER DelayInterval;
    DelayInterval.QuadPart = -10LL * 1000LL * 1000LL;
    UINT32 u32LastCompletedFenceId = 0;

    /* Wait until the host processes all submitted buffers to allow delays on the host (debug, etc). */
    for (;;)
    {
        BOOLEAN bReturnValue = FALSE;
        Status = pDxgkInterface->DxgkCbSynchronizeExecution(pDxgkInterface->DeviceHandle,
                                                            gaQueryCurrentFenceCb, pDevExt, 0, &bReturnValue);

        if (Status != STATUS_SUCCESS)
        {
            LOGE("DxgkCbSynchronizeExecution failed.\n");
            break;
        }

        u32LastCompletedFenceId = ASMAtomicReadU32(&pGaDevExt->u32LastCompletedFenceId);
        UINT32 const u32LastSubmittedFenceId = ASMAtomicReadU32(&pGaDevExt->u32LastSubmittedFenceId);
        if (u32LastCompletedFenceId == u32LastSubmittedFenceId)
        {
            break;
        }

        LOGI("hAdapter %p, LastCompletedFenceId %d, LastSubmittedFenceId %d...\n",
            hAdapter, u32LastCompletedFenceId, u32LastSubmittedFenceId);

        KeDelayExecutionThread(KernelMode, FALSE, &DelayInterval);
    }

    if (Status == STATUS_SUCCESS)
    {
        pCurrentFence->CurrentFence = u32LastCompletedFenceId;
    }

    LOGI("hAdapter %p, CurrentFence %d, Status 0x%x\n", hAdapter, pCurrentFence->CurrentFence, Status);

    return Status;
}

void GaDeviceDestroy(PVBOXWDDM_EXT_GA pGaDevExt,
                     PVBOXWDDM_DEVICE pDevice)
{
    /* Free fence objects and GMRs. This is useful when the application has crashed.. */
    GaFenceObjectsDestroy(pGaDevExt, pDevice);
    SvgaRegionsDestroy(pGaDevExt->hw.pSvga, pDevice);
    LOGI("pDevice %p\n", pDevice);
}
